某些情况下,我们需要让程序休眠一段时间再继续运行。
Node.js中通常的实现是这样的:
function
如果你需要休息2秒,则这样使用:
// do something
但实际上该 `sleep` 函数只对当前调用有效,无法阻止其它异步函数的继续执行。一个简单的证明:
[
运行上述代码,终端上出现如下信息:
我们看到,尽管 1 中调用了 sleep,2 和 3 几乎也在同一时间在运行着。尽管我们一共调用了3次 sleep,但整个程序基本上在2秒内就运行结束了。简单的解释,就这是这个版本的 sleep 并不能阻止 event loop 继续运行。
那么问题是,有没有好的办法阻塞 event loop 呢?
Yes,有人写了 atomic-sleep 模块:
davidmarkclements/atomic-sleepgithub.com作者对它的介绍是:
⏱️Zero CPU overhead, zero dependency, true event-loop blocking sleep ⏱️
即,零 CPU 开销、零依赖、真正阻塞event loop的sleep。
将上面代码中的 sleep 替换成 atomic-sleep 中的实现。
const
我们看看打印了什么:
可以看到,整个过程用了6秒,1 在 sleep 的过程中,2 和 3 被阻塞了。
源码解释
打开 atomic-sleep 模块的源码,在 index.js 文件中,我们看到其主要逻辑是依赖了 Atomics.wait 方法,该方法会监听一个Int32Array 对象的给定下标下的值,若值未发生改变,则一直等待(阻塞event loop),直到发生超时(由ms参数决定定):
const
另外,模块还对低版本的 js 运行时做了兼容,如果不支持 Atomics,则改用一个占用CPU运行的方式:
function
文章开头有说到,某些情况下,我们需要让程序休眠一段时间再继续运行。我提供一个具体的例子。
在写爬虫时,为了提升效率,我们通常会并发爬取一组网页。如果某个网页的请求返回了 429 异常码(Too Many Requests)。则休眠X秒再重试。我们已经知道基于 setTimeout 的休眠并不能阻止其它异步函数的执行(重试),这时 atomic-sleep 就派上用场了。
大致的爬取代码类似下面这样:
// 一组url
觉得有用?欢迎关注我的公众号,上面有更多技巧性文章!