2016-10-27

关于 setImmediate

W3C Draft

题目叫 “Efficient Script Yielding”,一份 2011 年的 “Editor's Draft”,从题目就能看出用途。建议有时间读一遍,超级短。摘要就一句话:

This specification defines an interface for web applications to flush the browser event queue and receive an immediate callback. 本说明文档定义了一个用于刷新浏览器事件队列、接收即时回调的 Web 应用接口。

MDN

MDN 的文档没得说。遇到问题去查查肯定不会害你,有时候运气好,还能读到翻译过来的中文版:

This method is used to break up long running operations and run a callback function immediately after the browser has completed other operations such as events and display updates. 该方法用来把一些需要长时间运行的操作放在一个回调函数里,在浏览器完成后面的其他语句后,就立刻执行这个回调函数。

但同时,文档提到, 只有 IE 10+ 和 Node.js 0.10+ 实现了该方法。setImmediate 受到了 Gecko 和 Webkit 的 “resistance”(抵制)。建议跟着去看看热闹。

MDN 文档中提到了三种模拟 setImmediate 的方式:postMessageMessageChannelsetTimeout(fn, 0)

setImmediate polyfill

对于 Node 0.9 之前的,使用 process.nextTick 模拟;对于非 IE 10 的现代浏览器,使用 postMessage;对 Web Worker,使用 MessageChannel(这个之后需要关注下);对 IE 6–8,向 html 中插入新的 script 标签,在 onreadystatechange 事件中执行回调;其他浏览器,统一使用 setTimeout(fn, 0) 的形式。

// Don't get fooled by e.g. browserify environments.
if ({}.toString.call(global.process) === "[object process]") {
    // For Node.js before 0.9
    installNextTickImplementation();

} else if (canUsePostMessage()) {
    // For non-IE10 modern browsers
    installPostMessageImplementation();

} else if (global.MessageChannel) {
    // For web workers, where supported
    installMessageChannelImplementation();

} else if (doc && "onreadystatechange" in doc.createElement("script")) {
    // For IE 6–8
    installReadyStateChangeImplementation();

} else {
    // For older browsers
    installSetTimeoutImplementation();
}

Nicholas C. Zakas 的文章

文章很短,但讲得还挺仔细的。作者提到了两点好处:

  • 可以直接在 UI 队列清空后直接插入 JS 任务;
  • 延迟更短,不必等待下一次 timer tick

Edge Demo

通过 250 个数的排序,来对比处理效率。基本原理是,排序时将每一步的交换操作放在回调中,对比排序完成的效率。一共有四种:

  • setTimeout(fn, 15)
  • setTimeout(fn, 4)
  • PostMessage
  • setImmediate

关于前两种的时间间隔问题,建议直接取读 demo 底部的说明。已经很详细了。

Stackoverflow

题外话

  • 以前竟然不知道 setTimeout 也能接收第三个第四个参数的。setTimeout((a, b) => a + b, 0, 22, 33)

  • !(x > 9)x <= 9 可不一定完全等价,前者在不确定 x 的类型时,特别有用,不用额外判断 undefined。