PS:
2018/08/08 轉簡體
2018/08/09 從新排版佈局,代碼全面使用ES6而且直接附上輸出結果,補充細節
2018/08/13 補充Async..await內容,增長例子說明
2018/08/20 新增Async..await和Promise.all在遍歷狀況下怎麼使用javascript
上文講了關於Javascript執行機制--單線程,同異步任務,事件循環的知識點,咱們知道Javascript在某一時刻內只能執行特定的一個任務,而且會阻塞其它任務執行,爲了解決這個問題,Javascript語言將任務的執行模式分紅兩種:html
如今咱們就講講關於異步編程的發展
更多細節請看阮一峯大神的《ECMAScript 6 入門》java
若是看不清晰右鍵圖片新標籤頁打開
node
回調函數就是一個經過函數指針調用的函數.若是你把函數的指針(地址)做爲參數傳遞給另外一個函數,當這個指針被用來調用其所指向的函數時,咱們就說這是回調函數.回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應.
⑴定義一個回調函數;
⑵提供函數實現的一方在初始化的時候,將回調函數的函數指針註冊給調用者;
⑶當特定的事件或條件發生的時候,調用者使用函數指針調用回調函數對事件進行處理.
例如:git
//定義一個回調函數 function callback() { console.log('I am a callback!'); } //函數實現 function trigger(fn, time) { setTimeout(function() { fn && fn(); }, time || 1000); } //耐心等兩秒哦 trigger(callback, 2000); //輸出 //I am a callback!
tips:由於異步任務在執行機制裏處理方式不一樣的問題,try/catch語句只能捕捉執行棧上的錯誤,詳情請回顧Javascript執行機制--單線程,同異步任務,事件循環github
也叫觀察者模式,這是一種常見的編程方式,一個對象(目標對象和觀察者對象)的狀態發生改變,全部的依賴對象(觀察者對象)都將獲得通知,進行廣播通知.執行事件由觸發事件調用與順序無關,易用低耦合而且不依賴函數調用.ajax
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <ul id="list"> <li id="item1">item1</li> <li id="item2">item2</li> <li id="item3">item3</li> </ul> <script type="text/javascript"> var list = document.getElementById("list"); document.addEventListener("click", function (event) { var target = event.target; if (target.nodeName == "LI") { alert(target.innerHTML); } }) </script> </body> </html>
這是一種相似事件監聽可是卻更強大的設計模式(不少人也把二者視爲同一種模式,我的以爲仍是有點區別的),例如:編程
例如咱們能夠本身封裝一個方法以實現更多監聽事件segmentfault
var listener = (function() { //訂閱隊列 var list = [], //監聽事件 on = function(type, fn) { if (!list[type]) list[type] = []; list[type].push(fn); }, //取消監聽事件 off = function(type, fn) { list[type] = []; }, //觸發事件 trigger = function() { //取出監聽類型 var type = Array.prototype.shift.call(arguments), queue = list[type], i = 0, len = queue.length; for (; i < len; i++) { //帶參數發佈 queue[i].apply(this, arguments); } }; return { on: on, off: off, trigger: trigger, }; })(); listener.on('log', function() { console.log('I trigger a log!'); }); listener.trigger('log'); //取消以後不觸發 listener.off('log'); listener.trigger('log'); //輸出 //I trigger a log!
上面的方法無論怎麼變化,始終避不開回調地獄的困境,直到後來被社區提出和實現而且已經被放置到ES6統一標準化的---Promise
出現.
簡單來講就兩個特性:設計模式
pending
(進行中)狀態只能經過異步操做的結果更改爲fulfilled/resolved
(已成功)或者rejected
(已失敗)的狀態,即其餘任何手段都不能影響到;const promise = new Promise((resolve, reject) => { true ? resolve('success') : reject('error'); }); //Promise實例 console.log(promise); promise.then( function resolved(res) { console.log(res); }, function rejected(err) { console.log(err); } ); //輸出 //Promise { } //success
Promise構造函數接受一個函數做爲參數,JavaScript 引擎提供兩個參數分別是:
參數名 | 用途 |
---|---|
resolve | 將Promise對象的狀態從pending(進行中)變爲resolved(已成功) |
reject | 將Promise對象的狀態從pending(進行中)變爲rejected(已失敗) |
其中rejected(已失敗)的狀態包括程序的錯誤也能捕捉而且不會中斷程序運行,並把錯誤緣由當成參數傳遞下去
const promise = new Promise(function(resolve, reject) { //未定義變量 return num; }); promise.then(null, function rejected(err) { console.log(`rejected:${err}`); }); //輸出 //rejected: ReferenceError: num is not defined
Promise 實例能訪問定義在原型對象Promise.prototype
上的then
方法.它的做用是爲 Promise 實例添加狀態改變時的回調函數,提供兩個參數分別是
參數名 | 用途 |
---|---|
resolved | resolved狀態的回調函數 |
rejected(可選) | 是rejected狀態的回調函數,若是發生錯誤但沒有處理函數會程序報錯而不是狀態失敗報錯 |
const promise = new Promise(function(resolve, reject) { throw 'error'; }); //有rejected處理函數 promise.then( function resolved(res) { console.log(res); }, function rejected(err) { console.log(`捕捉錯誤:${err}`); } ); //沒有rejected處理函數 promise.then(function resolved(res) { console.log(res); }); //捕捉錯誤:error //系統報錯
由於then返回的是一個新的Promise實例,因此能夠鏈式調用,若是須要把當前結果做爲下一個then方法的參數將它做爲返回值return.
const promise = new Promise((resolve, reject) => { true ? resolve('success') : reject('error'); }); promise .then(function resolved(res) { console.log(res); //此次不返回結果了 }) .then(function resolved(res) { console.log(res); //此次返回其餘 return '成功'; }) .then( function resolved(res) { console.log(`成功信息:${res}`); }, function rejected(err) { console.log(`失敗信息:${err}`); } ); //輸出 //success //undefined //成功信息:成功
Promise代碼量看著多隻是爲了讓讀者更直觀,採用ES6箭頭函數跟縮寫名稱能夠壓縮成下面這樣
const promise = new Promise ((resolve, reject) => { true ? resolve ('success') : reject ('error'); }); promise.then (res => res, err => err);
then方法會隱性返回一個Promise或者開發者顯性返回任何值,而Promise有本身內部機制處理:
若是回調函數中返回一個值或者不返回:
若是回調函數拋出錯誤:
若是回調函數返回一個Promise:
前面說過狀態一旦更改是不可逆,即便以後再添加回調函數也只是返回同個結果
即便後面再拋出錯誤then也是返回resolved狀態Promise;
const promise = new Promise ((resolve, reject) => { resolve (); throw 'error'; }); promise.then ( res => { console.log (`resloved: ${res}`); }, err => { console.log (`rejected: ${err}`); } ); //輸出 //resloved: undefined
即便後面再顯性返回resolved狀態Promise也是返回rejected狀態Promise;
const promise = new Promise ((resolve, reject) => { reject (); return Promise.resolve (); }); promise.then ( res => { console.log (`resloved: ${res}`); }, err => { console.log (`rejected: ${err}`); } ); //輸出 //rejected: undefined
基本條件以下:
//開始執行時間 console.log (`開始運行時間: ${new Date ().toTimeString ()}`); const p1 = new Promise ((resolve, reject) => { //3秒後執行 setTimeout (resolve, 3000); }), p2 = new Promise ((resolve, reject) => { //1秒後執行 setTimeout (() => resolve (p1), 1000); }); //處理時間 p2.then ( res => { console.log (`p2返回成功狀態時間: ${new Date ().toTimeString ()}`); }, err => { console.log (`p2返回失敗狀態時間: ${new Date ().toTimeString ()}`); } ); //輸出 //開始運行時間: 09:27:44 GMT+0800 (中國標準時間) //p2返回成功狀態時間: 09:27:47 GMT+0800 (中國標準時間)
//開始執行時間 console.log(`開始運行時間: ${new Date().toTimeString()}`); const p1 = new Promise((resolve, reject) => { //3秒後執行 setTimeout(reject, 3000); }), p2 = new Promise((resolve, reject) => { //1秒後執行 setTimeout(() => resolve(p1), 1000); }); //處理時間 p2.then( res => { console.log(`p2返回成功狀態時間: ${new Date().toTimeString()}`); }, err => { console.log(`p2返回失敗狀態時間: ${new Date().toTimeString()}`); } ); //輸出 //開始運行時間: 09:30:50 GMT+0800 (中國標準時間) //p2返回失敗狀態時間: 09:30:53 GMT+0800 (中國標準時間)
//開始執行時間 console.log (`開始運行時間: ${new Date ().toTimeString ()}`); const p1 = new Promise ((resolve, reject) => { //3秒後執行 setTimeout (resolve, 3000); }), p2 = new Promise ((resolve, reject) => { //1秒後執行 setTimeout (() => reject (p1), 1000); }); //處理時間 p2.then ( res => { console.log (`p2返回成功狀態時間: ${new Date ().toTimeString ()}`); }, err => { console.log (`p2返回失敗狀態時間: ${new Date ().toTimeString ()}`); } ); //輸出 //開始運行時間: 09:31:33 GMT+0800 (中國標準時間) //p2返回失敗狀態時間: 09:31:34 GMT+0800 (中國標準時間)
看完三個例子以後還有一個省略代碼能夠得出結論:
p1 | p2 | then | 間隔(s) |
---|---|---|---|
resolved | resolved | resolved | 3 |
rejectd | resolved | rejectd | 3 |
resolved | rejectd | rejectd | 1 |
rejectd | rejectd | rejectd | 1 |
結論:then回調函數的Promise狀態首先取決於調用函數(p2)狀態,當它resolved(已成功)狀況下才取決於返回函數Promise(p1)的狀態.
若是Promise狀態變爲rejected或者then方法運行中拋出錯誤,均可以用catch捕捉而且返回一個新的Promise實例,建議then方法省略錯誤處理,最底層添加一個catch處理機制;
const promise = new Promise((resolve, reject) => { throw 'error'; }); //省略then錯誤處理 promise.then().catch(res => { console.log(res); }); //輸出 //error
注意:若是決定使用catch處理的話前面就不能用reject作錯誤處理了,由於被攔截以後是不會再通過catch了
const promise = new Promise((resolve, reject) => { throw 'error'; }); promise .then(null, err => { console.log(`then: ${err}`); return err; }) .catch(err => { //跳過 console.log(`catch: ${err}`); }) .then( res => { console.log(`resloved: ${res}`); }, err => { console.log(`rejected: ${err}`); } ); //輸出 //then: error //resloved: error
從結果能夠看到第一個then有了錯誤處理函數以後會跳過catch方法,而後第二個then會在成功處理函數裏打印?說好的狀態一旦更改是不可逆呢???
這裏面又涉及到return值的問題了.
第一層then已經作了錯誤處理,返回一個resloved狀態Promise;
由於catch不會處理resloved狀態Promise因此跳過;
第二層then接收並在resloved處理函數處理;
若是記性好的人應該記得上面講解例子有個相似的寫法可是卻依然報錯,知道緣由麼?
const promise = new Promise(function(resolve, reject) { throw 'error'; }); //有rejected處理函數 promise.then( function resolved(res) { console.log(res); }, function rejected(err) { console.log(`捕捉錯誤:${err}`); } ); //沒有rejected處理函數 promise.then(function resolved(res) { console.log(res); }); //捕捉錯誤:error //系統報錯
再重複一遍,Promise每次都會返回一個新的Promise實例,因此不能用鏈式調用後的結果來看待變量promise.
方法 | 做用 |
---|---|
resolve | 快速返回一個新的 Promise 對象,狀態爲resolved的實例 |
reject | 快速返回一個新的 Promise 對象,狀態爲rejected的實例 |
finally | 無論 Promise 對象最後狀態如何,都會執行的操做,回調函數不接受任何參數這代表finally方法裏面的操做,應該是與狀態無關的,不依賴於 Promise 的執行結果。該方法是 ES2018 引入標準的,就不說了 |
The Promise.all(iterable) method returns a single Promise that resolves when all of the promises in the iterable argument have resolved or when the iterable argument contains no promises. It rejects with the reason of the first promise that rejects.
大概意思就是當迭代器參數裏的全部Promise都返回成功狀態或者沒有Promise入參的時候返回一個成功狀態Promise,不然返回第一個失敗狀態的Promise拋出的錯誤.
若是迭代器參數不是Promise也會被隱性調用Promise.resolve方法轉成Promis實例.(Promise.all方法的參數必須具備 Iterator 接口,且返回的每一個成員都是 Promise 實例).
//成功 const p1 = new Promise((resolve, reject) => { resolve('suc1'); }), //成功 p2 = new Promise((resolve, reject) => { resolve('suc2'); }), //失敗 p3 = new Promise((resolve, reject) => { reject('err'); }); Promise.all([p1, p2]).then(res => { console.log(`所有成功:${res}`); }); Promise.all([p1, p3]).then(null, err => console.log(`第一個失敗:${err}`)); Promise.all([]).then( res => console.log('空數組返回成功Promise'), err => console.log('空數組返回失敗Promise') ); //輸出 //空數組返回成功Promise //所有成功: (2) ["suc1", "suc2"] //第一個失敗: err
注意: 若是空數組狀況下立馬返回成功狀態,儘管放在最後位置卻第一個打印結果!
跟all相似,區別在於兩點:
//成功 const p1 = new Promise((resolve, reject) => { resolve('suc1'); }), //成功 p2 = new Promise((resolve, reject) => { resolve('suc2'); }), //失敗 p3 = new Promise((resolve, reject) => { reject('err'); }); Promise.race([p1, p2]).then(res => { console.log(`第一個返回成功:${res}`); }); Promise.race([p3, p2]).then(null, err => console.log(`第一個返回失敗:${err}`)); Promise.race([]).then( res => console.log('空數組返回成功Promise'), err => console.log('空數組返回失敗Promise') ); //輸出 //第一個返回成功:suc1 //第一個返回失敗:err
由於Promise的then方法會把函數結果放置到微任務隊列(micro tasks),也就是當次事件循環的最後執行.若是你使用的是同步函數其實是被無故延遲執行了.若是不清楚這方面內容能夠再看一下我以前寫得Javascript執行機制--單線程,同異步任務,事件循環
function sync() { console.log('sync'); } function async() { setTimeout(() => console.log('async'), 1000); } Promise.resolve().then(sync); Promise.resolve().then(async); console.log('end'); //輸出 //end //sync //async
實際上有兩種方法能夠實現讓同步函數同步執行,異步函數異步執行,而且讓它們具備統一的 API.
function sync() { console.log('sync'); } function async() { setTimeout(() => console.log('async'), 1000); } (() => new Promise(resolve => resolve(sync())))(); (() => new Promise(resolve => resolve(async())))(); console.log('end'); // 輸出 // sync // end // async
function sync() { console.log('sync'); } function async() { setTimeout(() => console.log('async'), 1000); } (async () => sync())(); (async () => async())(); console.log('end'); // 輸出 // sync // end // async
Generator 函數是 ES6 提供的一種異步編程解決方案.
語法上,首先能夠把它理解成,Generator 函數是一個狀態機,封裝了多個內部狀態.執行 Generator 函數並不執行裏面程序而是返回一個遍歷器對象,能夠依次遍歷 Generator 函數內部的每個狀態.yield表達式是暫停執行的標記,而next方法能夠恢復執行.
每一次調用next方法,都會返回數據結構的當前成員的信息.具體來講,就是返回一個包含value和done兩個屬性的對象.其中,value屬性是當前成員的值,done屬性是一個布爾值,表示遍歷是否結束.
function* Generator() { yield '1'; return '2'; } const example = Generator(); console.log(example); console.log(example.next()); console.log(example.next()); // 輸出 // Object [Generator] {} // { value: '1', done: false } // { value: '2', done: true }
yield表達式自己沒有返回值,或者說老是返回undefined.next方法能夠帶一個參數,該參數就會被看成上一個yield表達式的返回值.不然上一個yield表達式的返回值維持原狀不變,這個特性至關重要,咱們就依賴它從外部向內部注入不一樣的值,從而調整函數行爲.調用next的時候,內部指針會往下運行直到遇到下列狀況:
而後咱們根據規則再去解讀那一段代碼的運行結果
function* foo() { let x = yield 1; console.log(`函數內部x: ${x}`); let y = x * (yield x + 2); console.log(`函數內部xy: ${x} ${y}`); return x + y; } const it = foo(); console.log(it.next(2)); console.log(it.next(3)); console.log(it.next(4)); // 輸出 // 函數內部x: undefined // Object { value: 1, done: false } // 函數內部x: 3 // Object { value: 5, done: false } // 函數內部xy: 3 12 // Object { value: 15, done: true }
概念其實很清晰,過程有點複雜,你們謹記next方法能夠帶一個參數,該參數就會被看成上一個yield表達式的返回值.不然上一個yield表達式的返回值維持原狀不變,而後根據這個規則來算就明白了.
Generator函數內部,調用另外一個Generator函數,默認狀況下是沒有效果的,須要用到yield表達式,帶*會返回值,不帶*返回遍歷器對象.
function* foo() { bar(1); //內部調用Generator函數 yield bar(2); yield* bar(3); yield 1; } function* bar(num) { console.log(num); yield 2; } const it = foo(); console.log(it.next()); console.log(it.next()); console.log(it.next()); // 輸出 // { value: Object [Generator] {}, done: false } // 3 // { value: 2, done: false } // { value: 1, done: false }
從輸出結果可知只有yield * bar(3)生效.
若是被代理的 Generator 函數有return語句,那麼就能夠向代理它的Generator函數返回數據.
function* genFuncWithReturn() { yield 'a'; yield 'b'; return 'c'; } function* logReturned(genObj) { const result = yield* genObj; //返回值 console.log(result); } const it = logReturned(genFuncWithReturn()); console.log(it.next()); console.log(it.next()); console.log(it.next()); // 輸出 // { value: 'a', done: false } // { value: 'b', done: false } // c // { value: undefined, done: true }
function* foo() { console.log(1); console.log(2); return; } const it = foo(); console.log(3); it.next(); // 輸出 // 3 // 1 // 2
function foo() { yield 1; } const it = foo(); // 輸出 // Unexpected number
function* foo() { console.log('函數內部x: ' + (yield)); yield 1; } const it = foo(); console.log(it.next()); console.log(it.next(3)); // 輸出 // { value: undefined, done: false } // 函數內部x: 3 // { value: 1, done: false }
Generator函數返回的遍歷器對象的throw方法(不是全局的throw方法)能夠在函數體外拋出錯誤,這意味著出錯的代碼與處理錯誤的代碼,實現了時間和空間上的分離.而後在Generator函數體內被捕獲後,會附帶執行下一條yield表達式.
const err = function*() { try { yield; } catch (e) { console.log(`內部捕獲: ${e}`); } }; //執行 const it = err(); console.log(it.next()); //外部捕獲錯誤 try { //兩次拋出錯誤 console.log(it.throw('err1')); console.log(it.throw('err2')); } catch (e) { console.log(`外部捕獲: ${e}`); } // 輸出 // { value: undefined, done: false } // 內部捕獲 err1 // { value: undefined, done: true } // 外部捕獲 err2
因此這裏實際執行順序it.throw('err1') ⇨ it.next() ⇨ it.throw('err2')
同時,Generator函數體內拋出的錯誤,也能夠被函數體外的catch捕獲;
const err = function*() { yield '1'; throw 'err'; yield; yield '2'; }; //執行 const it = err(); console.log(it.next()); try { console.log(it.next()); } catch (e) { console.log('外部捕獲', e); } //捕捉錯誤以後繼續執行 console.log(it.next()); // 輸出 // { value: '1', done: false } // 外部捕獲 err // { value: undefined, done: true }
注意最後一次執行next函數返回{value: undefined, done: true}
,由於拋出錯誤後沒有作內部捕獲,JS引擎會認爲這個Generator函數已經遍歷結束.
返回給定的值或者不傳默認undefined,而且終結遍歷 Generator 函數.
function* Generator() { yield '1'; yield '2'; yield '3'; return '4'; } const it = Generator(); console.log(it.return(2)); console.log(it.next(3)); // 輸出 // { value: 2, done: true } // { value: undefined, done: true }
//模擬ajax請求 function ajax() { return new Promise((resolve, reject) => { setTimeout( () => resolve({ abc: 123, }), 1000 ); }); } function* foo() { try { yield; } catch (err) { console.log(`內部捕獲: ${err}`); } yield console.log('再次執行'); yield ajax(); return res; } const it = foo(); it.next(); it.throw('somethings happend'); it.next().value.then(res => { console.log(res); }); // 輸出 // 內部捕獲: somethings happend // 再次執行 // { abc: 123 }
暫停執行和恢復執行,這是Generator能封裝異步任務的根本緣由,強大的錯誤捕捉能力能夠在寫法上更加自由.
咱們能夠在上面的代碼擴展出更多步驟,例如這種異步依賴的代碼能夠以同步順序寫出來,或者外層操做res1以後再傳到ajax2等.
function* foo() { var res1 = yield ajax1(), res2 = yield ajax2(res1); return res; }
儘管Generator異步流程管理很是簡潔,可是操做流程不算方便,須要開發者決定何時執行下一步,甚至你會發如今整個異步流程會充斥著多個next的身影.
for...of循環能夠自動遍歷 Generator 函數時生成的Iterator對象,一旦next方法的返回對象的done屬性爲true,循環就會停止,且不包含該返回對象,因此return語句會執行,但不會顯示輸出.
function* Generator() { yield '1'; yield '2'; yield '3'; return '4'; } for (let v of Generator()) { console.log(v); } // 輸出 // 1 // 2 // 3
也能夠用while實現,區別在於for...of循環返回值,while返回數據結構
function* Generator() { yield '1'; yield '2'; yield '3'; return '4'; } let it = Generator(), result = it.next(); while (!result.done) { console.log(result); result = it.next(result); } // 輸出 // { value: '1', done: false } // { value: '2', done: false } // { value: '3', done: false }
上面方法不適用於異步操做,若是之間須要依賴關係的話可能會致使後續執行失敗,爲了實現這種效果有些很出名的衍生庫co等,關鍵源碼很短也比較簡單,直接貼出來了.
/** * Execute the generator function or a generator * and return a promise. * * @param {Function} fn * @return {Promise} * @api public */ function co(gen) { var ctx = this; var args = slice.call(arguments, 1); // we wrap everything in a promise to avoid promise chaining, // which leads to memory leak errors. // see https://github.com/tj/co/issues/180 return new Promise(function(resolve, reject) { if (typeof gen === 'function') gen = gen.apply(ctx, args); if (!gen || typeof gen.next !== 'function') return resolve(gen); onFulfilled(); /** * @param {Mixed} res * @return {Promise} * @api private */ function onFulfilled(res) { var ret; try { ret = gen.next(res); } catch (e) { return reject(e); } next(ret); return null; } /** * @param {Error} err * @return {Promise} * @api private */ function onRejected(err) { var ret; try { ret = gen.throw(err); } catch (e) { return reject(e); } next(ret); } /** * Get the next value in the generator, * return a promise. * * @param {Object} ret * @return {Promise} * @api private */ function next(ret) { if (ret.done) return resolve(ret.value); var value = toPromise.call(ctx, ret.value); if (value && isPromise(value)) return value.then(onFulfilled, onRejected); return onRejected( new TypeError( 'You may only yield a function, promise, generator, array, or object, ' + 'but the following object was passed: "' + String(ret.value) + '"' ) ); } }); }
完整代碼請看co,具體用法就不說了
ES2017 標準引入了 async 函數,一句話,它就是 Generator 函數的語法糖.
若是說 Promise 主要解決的是異步回調問題,那麼 async + await 主要解決的就是將異步問題同步化,下降異步編程的認知負擔.
function p1() { return Promise.resolve().then(res => console.log('p1')); } function p2() { return Promise.resolve().then(res => console.log('p2')); } async function Async() { console.log(1); await p1(); console.log(2); await p2(); } const it = Async().then(res => console.log(123)); // 輸出 // 1 // p1 // 2 // p2 // 123
async function Async() { return await 1; } Async().then(res => console.log(res)); // 輸出 // 1
async function Async() { await Promise.reject('err'); console.log('不執行了'); await Promise.resolve(); }; Async().catch(err => console.log(err)); // 輸出 // err
//阻塞主線程 const delazy = (time = 2000) => new Promise(resolve => { setTimeout(() => { resolve(); }, time); }); //模擬請求 async function quest() { await delazy(); return Promise.resolve({ str: '123', }); } //統一獲取 async function doSomethings() { const ary = await Promise.all([quest(), quest()]); console.log(ary.map(e => e.str)); } doSomethings();
async function Async() { try { await Promise.reject('err').then(null, err => console.log(err)); console.log('繼續執行'); await Promise.resolve('suc').then(res => console.log(res)); } catch (err) {} } Async(); // 輸出 // 繼續執行 // suc
function p1() { return new Promise((resolve, reject) => { setTimeout(() => resolve(1), 1000); }); } function p2() { return new Promise((resolve, reject) => { setTimeout(() => resolve(2), 1000); }); } async function sum() { const [a, b] = await Promise.all([p1(), p2()]); console.log(a, b); } ==================== OR ============================ async function sum() { const a = p1(), b = p2(), c = await a, d = await b; console.log(c, d); } sum(); // 輸出 // 1 2
跟上面說的co相似,將Generator函數和自動執行器,包裝在一個函數裏.
async function fn(args) {} //等價於 function fn(args) { return spawn(function*() {}); }
spawn函數就是自動執行器
function spawn(genF) { return new Promise(function(resolve, reject) { const gen = genF(); function step(nextF) { let next; try { 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); }); }); }
代碼量很少也不難,建議認真學習裏面的思路