漫谈如何终止 JS 程序的运行
最近在开发一个 JSSDK 的时候有一个需求:为了检测当前环境中是否已经加载过该脚本,需要在脚本开始运行时加入一层判断,如果检测到已存在该脚本导出的变量,则终止脚本的后续运行,否则再执行后续逻辑。 那么便碰上了本文标题的问题:如何终止 JS 程序的运行?
在很多其他语言比如 PHP 当中,存在 exit 函数来中止程序的运行。很遗憾的是 JavaScript 没有。因此我们需要自己模拟出这么一个 “exit” 功能。
我们知道,break
是 JavaScript 中合法的中断语句,但其只适用于循环和 switch 语句,此路不通。除了 break
,JavaScript 还可以通过 return
来显示终止一个函数的执行,比如:
function foo(){ console.log('executed'); return; console.log('not executed'); }
return
语句之后的代码将不会被执行。看起来可以用这个方式来达到我们终止 JS 脚本运行的目的。
那么我们来试试看在脚本中使用 return
:
// index.js console.log("OK,let's start"); console.log('first step'); console.log('second step'); return; console.log('you cannot see me');
我们把脚本丢到浏览器里去执行一下,发现报错了,信息如下:
Uncaught SyntaxError: Illegal return statement
这里我们犯了一个严重的错误:return
只能应用于函数内部,而脚本本身并不是函数。这里大家可能很自然的想到,把脚本包裹在一个自执行匿名函数(IIFE)里不就行了。我们尝试一下:
// index.js ;(function() { console.log("OK,let's start"); console.log('first step'); console.log('second step'); return; console.log('you cannot see me'); })();
浏览器的执行结果如下:
OK,let's start first step second step
OK,我们的目标达成,成功终止了脚本的运行。确实,将代码包裹在 IIFE 中可以随时控制脚本运行是否终止,但是这是否有点麻烦呢?固然我们有 Rollup 一类的工具可以将代码打包成为 IIFE 形式,但毕竟多一步操作,还需要对构建工具进行配置,在小项目里成本过高。而且,并不是所有需要中断程序运行的场景下都适合将代码通过 IIFE 来执行。因此,这一方式可行,但不够简洁。
实际可行的方法是利用错误来终止 JavaScript 程序的运行。这里的错误包括语法错误、变量错误、程序错误等等,我们只需要用 throw new Error()
的方式抛出错误,就能达到目的。在抛出该错误之前的代码可正常执行不受影响,而在其之后的代码则不会执行。看下例子:
// index.js console.log("OK,let's start"); console.log('first step'); console.log('second step'); throw new Error('this is my customed error'); console.log('you cannot see me');
结果如下:
OK,目标达成。你可以在你的程序中任意想中止的地方抛出错误,然后就能达到 exit 的目的。
但是,进一步思考,为什么 JavaScript 没有提供类似其他语言的 exit 函数呢?实际上,这一切都是因为 JavaScript 是单线程语言,基于代码可以异步执行的考虑,才没有设置 exit 功能。那么,是不是在包含异步执行的代码中,我们的抛出错误的方法实际上也不能立刻终止程序呢?答案是肯定的,我们看下例子:
// index.js console.log("OK,let's start"); console.log('first step'); console.log('second step'); setTimeout(() => { console.log('you still can see me'); }, 1000); throw new Error('this is my customed error'); console.log('you cannot see me');
执行结果如下:
“you still can see me” 延迟了一秒输出,但是仍然输出了,说明我们的 throw new Error
并没有立刻中止异步代码的执行。实际上这也是必然的,感兴趣的同学可以去研究一下 JavaScript 的运行机制以及 Event Loop 相关的内容。
最后,我们得出结论,JavaScript 的主线程同步任务可以通过抛出错误的方式立即中止,但是异步任务并不会受到影响。