[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)
- 步入JavaScript的世界
- 二进制运算
- JavaScript 的版本是怎么回事?
- JavaScript和DOM的产生与发展
- DOM事件处理
- js的并行加载与顺序执行
- 正则表达式
- 当遇上this时
- Javascript中apply、call、bind
- JavaScript的编译过程与运行机制
- 执行上下文(Execution Context)
- javascript 作用域
- 分组中的函数表达式
- JS之constructor属性
- Javascript 按位取反运算符 (~)
- EvenLoop 事件循环
- 异步编程
- JavaScript的九个思维导图
- JavaScript奇淫技巧
- JavaScript:shim和polyfill
- ===值得关注的库===
- ==文章==
- JavaScript框架
- Angular 1.x
- 启动引导过程
- $scope作用域
- $q与promise
- ngRoute 和 ui-router
- 双向数据绑定
- 规范和性能优化
- 自定义指令
- Angular 事件
- lodash
- Test