[TOC]

# [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