先看下面這道面試題:javascript
let counter = 0; const increment = new Promise(resolve => { counter++; resolve(); }); await increment; await increment; console.log(counter); // 結果是什麼?
你認爲 counter
的值是什麼?前端
什麼是 promise ?這是否意味着「稍後再作」?java
實際上應該把 promise 當作是一個狀態機。程序員
promise 以「pending」狀態開始它的生命週期。若是你要在這個狀態下查詢結果,則必須排隊。面試
根據 ECMAScript 標準文檔中的描述(https://tc39.es/ecma262/),上面 Promise
構造函數會當即調用咱們的執行器函數。它的 counter++
反作用運行。不帶任何參數調用resolve()
,在 promise 上存儲一個 undefined
結果,並將其狀態提高爲「fulfilled」。segmentfault
第一個 await
運行。 await
等同於在你的 promise 上運行.then(onFulfilled)
,將 onFulfilled
設置爲「前面的代碼」。這項工做做爲微任務進入隊列。 JavaScript 以先進先出的順序執行微任務;控制最終返回給咱們的函數。promise
第二個 await
沒有什麼不同的地方。它建立一個微任務給我 promise 的結果並提早運行代碼,而後等待 JavaScript 進行調度。服務器
反作用只在 Promise
構建期間運行過一次,因此 counter
爲 1
.微信
咱們推遲了工做嗎?沒有。promise 是同步建立和執行的。可是咱們使用了 await
來處理其餘微任務。多線程
那麼應該怎樣推遲工做?
const microtask = Promise.resolve() .then(() => console.log('hello from the microtask queue')); const macrotask = new Promise(resolve => // 排隊宏任務 setTimeout(() => { console.log('hello from the macrotask queue'); resolve(); }) // 沒有解決,promise 仍處於 pending 狀態 ); // 這是最早輸出的; 咱們成功地推遲了工做 console.log('hello from the original execution context'); // yield 調度器; 運行 .then() 並輸出日誌 await microtask; // yield 調度器; promise 的狀態是 fulfilled ,因此沒有日誌輸出 await microtask; // yield 調度器; 執行全部微任務,而後運行宏任務並輸出日誌 await macrotask; // yield 調度器; promise 已經完成,因此沒有日誌輸出 await macrotask;
Promise
構造函數是同步運行的,可是咱們沒必要同步調用 resolve()
。 Promise.prototype.then
也推遲了工做。
Promise
構造函數和 Promise.prototype.then
都不會重複工做。
這意味着 promise 能夠用來記住異步計算。若是你用了一個 promise,並且想要稍後再次用到它的結果,應該考慮保留 promise 而不是它的結果。