promise、async、await非同步原理與執行順序
async function async1(){
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2(){
console.log('async');
}
console.log('script start');
setTimeout(function (){
console.log('setTimeout');
},0);
async1();
new Promise(function(resolve){
console.log('promise1');
resolve();
}).then(function(){
console.log('promise2');
});
console.log('script end');
result:
script start
async1 start
async
promise1
script end
promise2
async1 end
undefined
setTimeout
MDN 是這樣描述 await 的:
我們(粗淺地)知道 await 之後的語句會等 await 表示式中的函式執行完得到結果後,才會繼續執行。
async 函式中可能會有 await 表示式,這會使 async 函式暫停執行,等待表示式中的 Promise 解析完成後繼續執行 async 函式並返回解決結果。
阮一峰老師的解釋我覺得更容易理解:
async 函式返回一個 Promise 物件,當函式執行的時候,一旦遇到 await 就會先返回,等到觸發的非同步操作完成,再接著執行函式體內後面的語句。
MDN 描述的暫停執行,實際上是讓出了執行緒(跳出 async 函式體)然後繼續執行後面的指令碼的。這樣一來我們就明白了,所以我們再看看上面那道題,按照這樣描述那麼他的輸出結果就應該是:
async
async function 宣告將定義一個返回 AsyncFunction 物件的非同步函式。
當呼叫一個 async 函式時,會返回一個 Promise 物件。當這個 async 函式返回一個值時,Promise 的 resolve 方法會負責傳遞這個值;當 async 函式丟擲異常時,Promise 的 reject 方法也會傳遞這個異常值。
所以你現在知道咯,使用 async 定義的函式,當它被呼叫時,它返回的其實是一個 Promise 物件。 我們再來看看 await 表示式執行會返回什麼值。
await
語法:[return_value] = await expression;
表示式(express):一個 Promise 物件或者任何要等待的值。
返回值(return_value):返回 Promise 物件的處理結果。如果等待的不是 Promise 物件,則返回該值本身。
所以,當 await 操作符後面的表示式是一個 Promise 的時候,它的返回值,實際上就是 Promise 的回撥函式 resolve 的引數。
明白了這兩個事情後,我還要再囉嗦兩句。我們都知道 Promise 是一個立即執行函式,但是他的成功(或失敗:reject)的回撥函式 resolve 卻是一個非同步執行的回撥。當執行到 resolve() 時,這個任務會被放入到回撥佇列中,等待呼叫棧有空閒時事件迴圈再來取走它。
終於進入正文:解題
執行到 async1 這個函式時,首先會列印出 “async1 start”(這個不用多說了吧,async 表示式定義的函式也是立即執行的);
然後執行到 await async2(),發現 async2 也是個 async 定義的函式,所以直接執行了 “console.log(‘async2’)”,同時 async2 返回了一個 Promise,劃重點:此時返回的 Promise 會被放入到回撥佇列中等待,await 會讓出執行緒(js 是單執行緒還用我介紹嗎),接下來就會跳出 async1 函式 繼續往下執行。
然後執行到 new Promise,前面說過了 promise 是立即執行的,所以先列印出來 “promise1”,然後執行到 resolve 的時候,resolve 這個任務就被放到回撥佇列中(前面都講過了上課要好好聽啊喂)等待,然後跳出 Promise 繼續往下執行,輸出 “script end”。
接下來是重頭戲。同步的事件都迴圈執行完了,呼叫棧現在已經空出來了,那麼事件迴圈就會去回撥佇列裡面取任務繼續放到呼叫棧裡面了。
這時候取到的第一個任務,就是前面 async1 放進去的 Promise,執行 Promise 時發現又遇到了他的真命天子 resolve 函式,劃重點:這個 resolve 又會被放入任務佇列繼續等待,然後再次跳出 async1 函式 繼續下一個任務。
接下來取到的下一個任務,就是前面 new Promise 放進去的 resolve 回撥 啦 yohoo~這個 resolve 被放到呼叫棧執行,並輸出 “promise2”,然後繼續取下一個任務。
後面的事情相信你已經猜到了,沒錯呼叫棧再次空出來了,事件迴圈就取到了下一個任務:歷經千辛萬苦終於輪到的那個 Promise 的 resolve 回撥!!!執行它(啥也不會列印的,因為 async2 並沒有 return 東西,所以這個 resolve 的引數是 undefined),此時 await 定義的這個 Promise 已經執行完並且返回了結果,所以可以繼續往下執行 async1 函式 後面的任務了,那就是 “console.log(‘async1 end’)”。
謎之困惑的那兩句執行結果(“promise2”、“async1 end”)就是這樣來的~
總結
總結下來這道題目考的,其實是以下幾個點:
- 呼叫棧-後進先出
- 事件迴圈
- 任務佇列
- promise 的回撥函式執行
- async 表示式的返回值
- await 表示式的作用和返回值
async和await的特點:
async:
1、async函式實質返回也是promise
2、加在一個函式前,可將其實現promise效果,函式中的return直接對應resolve
await:
1、await函式返回值也是promise
2、不可用作頂行,需放在async函式中使用
3、await後可跟promise, await,也可跟普通函式(就和普通函式一樣)
4、等待拿取後跟函式的值,並非阻塞執行緒,後續任務仍可非同步執行
5、有了await將很少用到then, 因為await是等待執行的
// async和await和try catch和結合,可以實現非同步的異常處理控制
async function f() {
await Promise.reject(new Error('whoops!'))
}
// 和下面一樣
async function f() {
throw new Error('Whoops!')
}
var getReturn = await Promise.resolve(1) // 這樣就不再需要另外的then接收promise的返回值,await直接等待返回值
資料參考
關於 async 和 await 的執行順序這裡
https://segmentfault.com/a/1190000011296839
關於呼叫棧、事件迴圈、任務佇列可以點這裡
https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-event-loop-and-the-rise-of-async-programming-5-ways-to-better-coding-with.md
async和await詳解
https://segmentfault.com/a/1190000013292562?utm_source=channel-newest
相關文章
- async await、Promise、setTimeout執行順序AIPromise
- promise、async和await之執行順序的那點事PromiseAI
- 事件迴圈 EventLoop(Promise,setTimeOut,async/await執行順序)事件OOPPromiseAI
- 令人費解的 async/await 執行順序AI
- Js中async/await的執行順序詳解JSAI
- JS中的async/await的執行順序詳解JSAI
- Js中process.nextTick,setImmediate,setTimeout,Promise.then,async/await終極非同步執行順序全解析JSPromiseAI非同步
- 8 張圖幫你一步步看清 async/await 和 promise 的執行順序AIPromise
- 8張圖幫你一步步看清 async/await 和 promise 的執行順序AIPromise
- 8張圖讓你一步步看清 async/await 和 promise 的執行順序AIPromise
- 理解非同步之美:Promise 與 async await(二)非同步PromiseAI
- 理解非同步之美:Promise與async await(一)非同步PromiseAI
- Promise與async/await與GeneratorPromiseAI
- 關於 Promise 的執行順序Promise
- 同步任務與非同步任務執行順序非同步
- 單例模式,promise與async/await單例模式PromiseAI
- [完結篇] - 理解非同步之美 --- promise與async await (三)非同步PromiseAI
- [Javascript] Promise question with async awaitJavaScriptPromiseAI
- JS非同步之callback、promise、async+await簡介JS非同步PromiseAI
- JS非同步程式設計 (2) – Promise、Generator、async/awaitJS非同步程式設計PromiseAI
- 一道setTimeout async promise執行順序的筆試題引發的思考Promise筆試
- 多執行緒合集(二)---非同步的那些事,async和await原理拋析執行緒非同步AI
- async await函式效能與Promise併發AI函式Promise
- 與Promise血脈相連的async/awaitPromiseAI
- [一道題搞蒙你] - setTimeout 與 Promise 執行順序Promise
- Async/Await 代替 Promise.all()AIPromise
- Promise和async await詳解PromiseAI
- Promise/async/await 研究筆記PromiseAI筆記
- Oracle SQL語句執行流程與順序原理解析OracleSQL
- async/await 和 promise/promise.all 的示例AIPromise
- 從一道Promise執行順序的題目看Promise實現Promise
- 命令執行順序控制與管道
- Vue 中 Promise 的then方法非同步使用及async/await 非同步使用總結VuePromise非同步AI
- 【理解ES7async/await並實現】手把手進行ES6非同步程式設計:Generator + Promise = Async/AwaitAI非同步程式設計Promise
- 非同步解決方案—-Promise與Await非同步PromiseAI
- 非同步解決方案----Promise與Await非同步PromiseAI
- 重構:從Promise到Async/AwaitPromiseAI
- Promise && async/await的理解和用法PromiseAI