摘要:它細說了什么是函數,以及其相較于的優勢。從最早的回調函數,到對象,再到函數,每次都有所改進,但又讓人覺得不徹底。所以,不能把它和回調函數搭配使用。但不用寫及其回調函數,這減少代碼行數,也避免了代碼嵌套。總結的異步編寫方式,從回調函數到再到。
前言
介于上一篇 「今日頭條」前端面試題和思路解析 中提到的 async/await,讓我想起了之前寫過的一篇文章,在此做個分享。它細說了什么是async函數,以及其相較于 Promise 的優勢。
溫故而知新,正文開始。
async 函數是什么?談及異步回調函數的嵌套,總會讓人感到煩躁,特別是當業務邏輯復雜,往往需要調用幾次 ajax 才能拿到所有需要的數據。
從最早的回調函數,到 Promise 對象,再到 Generator 函數,每次都有所改進,但又讓人覺得不徹底。它們都有額外的復雜性,都需要理解抽象的底層運行機制。所以,我們需要一種方法,更優雅地解決異步操作。于是,async函數出現了。
一句話解釋:async 函數,就是 Generator 函數的語法糖。
它有以下幾個特點:
建立在promise之上。所以,不能把它和回調函數搭配使用。但它會聲明一個異步函數,并隱式地返回一個Promise。因此可以直接return變量,無需使用Promise.resolve進行轉換。
和promise一樣,是非阻塞的。但不用寫 then 及其回調函數,這減少代碼行數,也避免了代碼嵌套。而且,所有異步調用,可以寫在同一個代碼塊中,無需定義多余的中間變量。
它的最大價值在于,可以使異步代碼,在形式上,更接近于同步代碼。
它總是與await一起使用的。并且,await 只能在 async 函數體內。
await 是個運算符,用于組成表達式,它會阻塞后面的代碼。如果等到的是 Promise 對象,則得到其 resolve值。否則,會得到一個表達式的運算結果。
為何說 async 函數是語法糖async 函數的實現,其實就是將 Generator 函數和自動執行器,包裝在一個函數里。下面的這個例子,來自阮老師的 《async 函數的含義和用法》 一文。
async function fn(args) { // ... } // 等同于 function fn(args) { return spawn(function*() { // ... }); } // spawn 函數就是自動執行器 function spawn(genF) { return new Promise(function(resolve, reject) { var gen = genF(); function step(nextF) { try { var next = nextF(); } catch(e) { return reject(e); } if(next.done) { return resolve(next.value); } Promise.resolve(next.value).then(function(v) { step(function() { return gen.next(v); }); }, function(e) { step(function() { return gen.throw(e); }); }); } step(function() { return gen.next(undefined); }); }); }
所以說,async 函數是 Generator 函數的語法糖。另外,它相對較新,屬于ES7中的語法。但是轉碼器 Babel 已經支持,轉碼后就能使用。
async 相較于 Promise 的優勢 1.相比于 Promise,它能更好地處理then鏈function takeLongTime(n) { return new Promise(resolve => { setTimeout(() => resolve(n + 200), n); }); } function step1(n) { console.log(`step1 with ${n}`); return takeLongTime(n); } function step2(n) { console.log(`step2 with ${n}`); return takeLongTime(n); } function step3(n) { console.log(`step3 with ${n}`); return takeLongTime(n); }
現在用 Promise 方式來實現這三個步驟的處理。
function doIt() { console.time("doIt"); const time1 = 300; step1(time1) .then(time2 => step2(time2)) .then(time3 => step3(time3)) .then(result => { console.log(`result is ${result}`); }); } doIt(); // step1 with 300 // step2 with 500 // step3 with 700 // result is 900
如果用 async/await 來實現的話,會是這樣:
async function doIt() { console.time("doIt"); const time1 = 300; const time2 = await step1(time1); const time3 = await step2(time2); const result = await step3(time3); console.log(`result is ${result}`); } doIt();
結果和之前的 Promise 實現是一樣的,但是這個代碼看起來是不是清晰得多,幾乎跟同步代碼一樣。
2.中間值現在把業務要求改一下,仍然是三個步驟,但每一個步驟都需要之前每個步驟的結果。Pomise的實現看著很暈,傳遞參數太過麻煩。
function doIt() { console.time("doIt"); const time1 = 300; step1(time1) .then(time2 => { return step2(time1, time2) .then(time3 => [time1, time2, time3]); }) .then(times => { const [time1, time2, time3] = times; return step3(time1, time2, time3); }) .then(result => { console.log(`result is ${result}`); }); } doIt();
用 async/await 來寫:
async function doIt() { console.time("doIt"); const time1 = 300; const time2 = await step1(time1); const time3 = await step2(time1, time2); const result = await step3(time1, time2, time3); console.log(`result is ${result}`); } doIt();
沒有多余的中間值,更加優雅地實現了。
3.調試相比于 Promise 更易于調試。
因為沒有代碼塊,所以不能在一個返回的箭頭函數中設置斷點。如果你在一個 .then 代碼塊中使用調試器的步進(step-over)功能,調試器并不會進入后續的 .then 代碼塊,因為調試器只能跟蹤同步代碼的每一步。
現在,如果使用 async/await,你就不必再使用箭頭函數。你可以對 await 語句執行步進操作,就好像他們都是普通的同步語句一樣。
總結JavaScript的異步編寫方式,從 回調函數 到 Promise、Generator 再到 Async/Await。表面上只是寫法的變化,但本質上則是語言層的一次次抽象。讓我們可以用更簡單的方式實現同樣的功能,而不需要去考慮代碼是如何執行的。
換句話說就是:異步編程的最高境界,就是根本不用關心它是不是異步。
參考文獻async 函數的含義和用法
(譯) 6個Async/Await優于Promise的方面
PS:歡迎關注我的公眾號 “超哥前端小棧”,交流更多的想法與技術。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100306.html
摘要:一篇文章和一道面試題最近,有篇名為張圖幫你一步步看清和的執行順序的文章引起了我的關注。作者用一道年今日頭條的前端面試題為引子,分步講解了最終結果的執行原因。從字面意思理解,讓我們等等。當前的最新版本,在這里的執行順序上,的確存在有問題。 一篇文章和一道面試題 最近,有篇名為 《8張圖幫你一步步看清 async/await 和 promise 的執行順序》 的文章引起了我的關注。 作者用...
摘要:接下來,我們一起來看看中的異步編程,具體有哪幾種。實現異步編程的方法一回調函數上面不止一次提到了回調函數。它是異步編程中,最基本的方法。四對象接下來,我們聊聊與相關的異步編程方法,對象。 showImg(https://segmentfault.com/img/bVbneWy?w=1600&h=1200); 前言 最近,小伙伴S 問了我一段代碼: const funB = (value...
摘要:而函數的命令后面則可以是或者原始類型的值,,,但這時等同于同步操作返回值是。拋出的錯誤而會被方法回調函數接收到。 ES7 提出的async 函數,終于讓 JavaScript 對于異步操作有了終極解決方案。No more callback hell。async 函數是 Generator 函數的語法糖。使用 關鍵字 async 來表示,在函數內部使用 await 來表示異步。想較于 G...
摘要:換句話說,當一個異步過程調用發出后,調用者不會立刻得到結果。參考文章珠峰架構課墻裂推薦細說異步函數發展歷程異步編程謝謝各位小伙伴愿意花費寶貴的時間閱讀本文,如果本文給了您一點幫助或者是啟發,請不要吝嗇你的贊和,您的肯定是我前進的最大動力。知其然知其所以然,首先了解三個概念: 1.什么是同步? 所謂同步,就是在發出一個調用時,在沒有得到結果之前,該調用就不返回。但是一旦調用返回,就得到返回值了...
摘要:參考文章珠峰架構課墻裂推薦細說異步函數發展歷程異步編程謝謝各位小伙伴愿意花費寶貴的時間閱讀本文,如果本文給了您一點幫助或者是啟發,請不要吝嗇你的贊和,您的肯定是我前進的最大動力。 知其然知其所以然,首先了解三個概念: 1.什么是同步? 所謂同步,就是在發出一個調用時,在沒有得到結果之前,該調用就不返回。但是一旦調用返回,就得到返回值了。換句話說,就是由調用者主動等待這個調用的結果。此調...
閱讀 4020·2021-11-22 13:53
閱讀 3617·2021-11-19 11:29
閱讀 1265·2021-09-08 09:35
閱讀 3162·2020-12-03 17:26
閱讀 514·2019-08-29 16:06
閱讀 2106·2019-08-26 13:50
閱讀 1180·2019-08-23 18:32
閱讀 2153·2019-08-23 18:12