ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] ![](https://box.kancloud.cn/d8a28e1b0015a0ab3a29de7b9ca72e13_1346x746.png) # [setTimeout(fn, 0) 和 setTimeout(fn, 1) 之间的区别? > https://stackoverflow.com/questions/8341803/what-is-the-difference-between-settimeoutfn-0-and-settimeoutfn-1 For Node.js,`0`is converted to`1`, so they are exactly the same:[https://github.com/nodejs/node/blob/master/lib/timers.js#L319](https://github.com/nodejs/node/blob/master/lib/timers.js#L319), and result might be: # 从promise、process.nextTick、setTimeout出发,谈谈Event Loop中的Job queue Event Loop 都不陌生,是指主线程从“任务队列”中循环读取任务,比如 例1: ```js setTimeout(function(){ console.log(1) },0); console.log(2) // 输出 2 1 ``` 在上述的例子中,我们明白首先执行主线程中的同步任务,当主线程任务执行完毕后,再从event loop中读取任务,因此先输出 2,再输出 1。 Event Loop 读取任务的先后顺序,取决于任务队列(Job queue)中对于不同任务读取规则的限定。比如下面一个例子: 例2: ```js setTimeout(function () { console.log(1); }, 0); Promise.resolve().then(function () { console.log(2); }); new Promise(resolve=>{ console.log(3) resolve() }).then(()=>{ console.log(4) }) console.log(5); // 输出为 3 5 2 4 1 ``` 先输出1,没有问题,因为是同步任务在主线程中优先执行,这里的问题是`setTimeout`和`Promise.then`任务的执行优先级是如何定义的。 # 任务队列(Job queue)中的执行顺序 在 Job queue 中的队列分为两种类型:**macro-task** 和 **micro-task**。我们举例来看执行顺序的规定,我们设 macro-task 队列包含任务: **a1, a2 , a3** micro-task 队列包含任务: **b1, b2 , b3** ## 执行顺序 1. 先执行 marco-task 队列开头的任务,也就是 **a1** 任务,执行完毕后; 2. 执行 micro-task 队列里的所有任务,也就是依次执行**b1, b2 , b3**,执行完后**清空 micro-task 中已存在的所有任务**; 3. 接着执行 marco-task 中的第二个任务,依次循环。 了解完了 `macro-task` 和`micro-task` 两种队列的执行顺序之后,我们接着来看,真实场景下这两种类型的队列里真正包含的任务(我们以 node V8 引擎为例),在node V8中,这两种类型的真实任务顺序如下所示: > 可以参考:node.js系列中的 Event Loop **macro-task** 队列真实包含任务: * script 标签(主程序代码) * setTimeout * setInterval * setImmediate(Node.js 环境) * I/O * UI rendering\交互事件 * postMessage * MessageChannel **micro-task** 队列真实包含任务(**一种尽快执行异步函数的方法,而不是放在调用堆栈的末尾**): * `process.nextTick` * `Promises` * `Object.observe` * `MutationObserver` 由此我们得到的执行顺序应该为: **script(主程序代码) —> process.nextTick —> Promises… ——> setTimeout ——> setInterval ——>setImmediate——> I/O ——> UI rendering** 在ES6中 macro-task 队列又称为 `ScriptJobs`,而 micro-task 又称 `PromiseJobs` # 真实环境中执行顺序的举例 (0) ``` const bar = () => console.log('bar') const baz = ()=> console.log('baz') const foo = () => { console.log('foo') setTimeout(bar, 0) new Promise((resolve, reject) => resolve('should be right after baz, before bar') ).then(resolve => console.log(resolve)) baz() } foo() //====== foo baz should be right after baz, before bar bar ``` 在当前函数结束之前 resolve 状态的 Promises 将在当前函数之后立即执行。 (1) `setTimeout`和`promise` 例3: ~~~ setTimeout(function () { console.log(3); }, 0); Promise.resolve().then(function () { console.log(2); }); console.log(1); ~~~ 我们先以第1小节的例子为例,这里遵循的顺序为: **script(主程序代码)——>promise——>setTimeout** 对应的输出依次为:1 ——>2————>3 (2) `process.nextTick`和`promise`、`setTimeout` 例子4: ~~~ setTimeout(function(){console.log(1)},0); new Promise(function(resolve,reject){ console.log(2); resolve(); }).then(function(){console.log(3) }).then(function(){console.log(4)}); process.nextTick(function(){console.log(5)}); console.log(6); //输出2,6,5,3,4,1 ~~~ 这个例子就比较复杂了,这里要注意的一点在定义 promise 的时候,promise 构造部分是同步执行的,这样问题就迎刃而解了。 首先分析Job queue 的执行顺序: **script(主程序代码)——>process.nextTick——>promise——>setTimeout** I)**主体部分**: 定义promise的构造部分是同步的, 因此先输出2 ,主体部分再输出6(同步情况下,就是严格按照定义的先后顺序) II)**process.nextTick**: 输出5 III)**promise**: 这里的 promise 部分,**严格的说其实是 `promise.then` 部分**,输出的是3,4 IV) **setTimeout** : 最后输出1 综合的执行顺序就是: 2——>6——>5——>3——>4——>1 3)更复杂的例子 ```js setTimeout(function(){ console.log(1) },0); new Promise(function(resolve,reject){ console.log(2); setTimeout(function(){resolve()},0) }).then(function(){ console.log(3) }).then(function(){ console.log(4) }); process.nextTick(function(){ console.log(5) }); console.log(6); //输出的是 2 6 5 1 3 4 ``` 这种情况跟我们(2)中的例子,区别在于`promise`的构造中,没有同步的`resolve`,因此`promise.then`在当前的执行队列中是不会触发的,只有`promise`从`pending`转移到`resolve`,才触发`then`方法,而这个`resolve`是在一个`setTimout`时间中完成的,因此**3,4**最后输出。 # 参考 [The JavaScript Event Loop: Explained](https://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/) [事件循环机制 Event-Loop及其延伸](https://github.com/amandakelake/blog/issues/26) [JavaScript 执行机制](https://www.manster.me/?p=711) [macrotask与microtask](http://www.ayqy.net/blog/javascript-macrotask-vs-microtask/) [从promise、process.nextTick、setTimeout出发,谈谈Event Loop中的Job queue](https://blog.csdn.net/liwusen/article/details/79509288) [Promise的队列与setTimeout的队列有何关联?](https://www.zhihu.com/question/36972010)