1、async/await的優勢node
1)方便級聯調用:即調用依次發生的場景;python
2)同步代碼編寫方式: Promise使用then函數進行鏈式調用,一直點點點,是一種從左向右的橫向寫法;async/await從上到下,順序執行,就像寫同步代碼同樣,更符合代碼編寫習慣;編程
3)多個參數傳遞: Promise的then函數只能傳遞一個參數,雖然能夠經過包裝成對象來傳遞多個參數,可是會致使傳遞冗餘信息,頻繁的解析又從新組合參數,比較麻煩;async/await沒有這個限制,能夠當作普通的局部變量來處理,用let或者const定義的塊級變量想怎麼用就怎麼用,想定義幾個就定義幾個,徹底沒有限制,也沒有冗餘工做;多線程
4)同步代碼和異步代碼能夠一塊兒編寫: 使用Promise的時候最好將同步代碼和異步代碼放在不一樣的then節點中,這樣結構更加清晰;async/await整個書寫習慣都是同步的,不須要糾結同步和異步的區別,固然,異步過程須要包裝成一個Promise對象放在await關鍵字後面;併發
5)基於協程: Promise是根據函數式編程的範式,對異步過程進行了一層封裝,async/await基於協程的機制,是真正的「保存上下文,控制權切換……控制權恢復,取回上下文」這種機制,是對異步過程更精確的一種描述;異步
6)async/await是對Promise的優化: async/await是基於Promise的,是進一步的一種優化,不過在寫代碼時,Promise自己的API出現得不多,很接近同步代碼的寫法;async
2、協程函數式編程
// 傳統的生產者-消費者模型是一個線程寫消息,一個線程取消息,經過鎖機制控制隊列和等待,但一不當心就可能死鎖。 // 若是改用協程,生產者生產消息後,直接經過yield跳轉到消費者開始執行,待消費者執行完畢後,切換回生產者繼續生產,效率極高: import time def consumer(): r = '' while True: n = yield r if not n: return print('[CONSUMER] Consuming %s...' % n) time.sleep(1) r = '200 OK' def produce(c): c.next() n = 0 while n < 5: n = n + 1 print('[PRODUCER] Producing %s...' % n) r = c.send(n) print('[PRODUCER] Consumer return: %s' % r) c.close() if __name__=='__main__': c = consumer() produce(c)
[PRODUCER] Producing 1... [CONSUMER] Consuming 1... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 2... [CONSUMER] Consuming 2... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 3... [CONSUMER] Consuming 3... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 4... [CONSUMER] Consuming 4... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 5... [CONSUMER] Consuming 5... [PRODUCER] Consumer return: 200 OK
注意到consumer函數是一個generator(生成器),把一個consumer傳入produce後:函數
首先調用c.next()啓動生成器;性能
而後,一旦生產了東西,經過c.send(n)切換到consumer執行;
consumer經過yield拿到消息,處理,又經過yield把結果傳回;
produce拿到consumer處理的結果,繼續生產下一條消息;
produce決定不生產了,經過c.close()關閉consumer,整個過程結束。
整個流程無鎖,由一個線程執行,produce和consumer協做完成任務,因此稱爲「協程」,而非線程的搶佔式多任務。
3、async關鍵字
1)代表程序裏面可能有異步過程: async關鍵字代表程序裏面可能有異步過程,裏面能夠有await關鍵字;固然所有是同步代碼也不要緊,可是這樣async關鍵字就顯得多餘了;
2)非阻塞: async函數裏面若是有異步過程會等待,可是async函數自己會立刻返回,不會阻塞當前線程,能夠簡單認爲,async函數工做在主線程,同步執行,不會阻塞界面渲染,async函數內部由await關鍵字修飾的異步過程,工做在相應的協程上,會阻塞等待異步任務的完成再返回;
3)async函數返回類型爲Promise對象: 這是和普通函數本質上不一樣的地方,也是使用時重點注意的地方;
(1)return newPromise();這個符合async函數本意;
(2)return data;這個是同步函數的寫法,這裏是要特別注意的,這個時候,其實就至關於Promise.resolve(data);仍是一個Promise對象,可是在調用async函數的地方經過簡單的=是拿不到這個data的,由於返回值是一個Promise對象,因此須要用.then(data => { })函數才能夠拿到這個data;
(3)若是沒有返回值,至關於返回了Promise.resolve(undefined);
4)無等待 聯想到Promise的特色,在沒有await的狀況下執行async函數,它會當即執行,返回一個Promise對象,而且絕對不會阻塞後面的語句,這和普通返回Promise對象的函數並沒有二致;
5)await不處理異步error: await是無論異步過程的reject(error)消息的,async函數返回的這個Promise對象的catch函數負責統一抓取內部全部異步過程的錯誤;async函數內部只要有一個異步過程發生錯誤,整個執行過程就中斷,這個返回的Promise對象的catch就能抓取到這個錯誤;
5)async函數的執行: async函數執行和普通函數同樣,函數名帶個()就能夠了,參數個數隨意,沒有限制,也須要有async關鍵字;只是返回值是一個Promise對象,能夠用then函數獲得返回值,用catch抓整個流程中發生的錯誤;
async function testAsync() { return "hello async"; } const result = testAsync(); // 返回一個Promise對象 console.log(result); // async函數返回的是一個Promise對象,async函數(包括函數語句、函數表達式、Lambda表達式)會返回一個Promise對象,若是在函數中return一個直接量,async會把這個直接量經過Promise.resolve() 封裝成 Promise 對象;
// async函數返回的是一個Promise對象,因此在最外層不能用await獲取其返回值的狀況,應該使用原始的方式:then()鏈來處理這個Promise對象 testAsync().then(v => { console.log(v); // 輸出 hello async });
4、await關鍵字
1)await只能在async函數內部使用:不能放在普通函數裏面,不然會報錯;
2)await關鍵字後面跟Promise對象:在Pending狀態時,相應的協程會交出控制權,進入等待狀態,這是協程的本質;
3)await是async wait的意思: wait的是resolve(data)的消息,並把數據data返回,好比下面代碼中,當Promise對象由Pending變爲Resolved的時候,變量a就等於data,而後再順序執行下面的語句console.log(a),這真的是等待,真的是順序執行,表現和同步代碼幾乎如出一轍;
const a = await new Promise((resolve, reject) => { // async process ... return resolve(data); }); console.log(a);
4)await後面也能夠跟同步代碼: 不過系統會自動將其轉化成一個Promsie對象,好比:
const a = await 'hello world' // 至關於 const a = await Promise.resolve('hello world'); // 跟同步代碼是同樣的,還不如省事點,直接去掉await關鍵字 const a = 'hello world';
5)await對於失敗消息的處理: await只關心異步過程成功的消息resolve(data),拿到相應的數據data,至於失敗消息reject(error),不關心不處理;對於錯誤的處理有如下幾種方法供選擇:
(1)讓await後面的Promise對象本身catch;
(2)也可讓外面的async函數返回的Promise對象統一catch;
(3)像同步代碼同樣,放在一個try...catch結構中;
async componentDidMount() { // 這是React Native的回調函數,加個async關鍵字,沒有任何影響,可是能夠用await關鍵字 // 將異步和同步的代碼放在一個try..catch中,異常都能抓到 try { let array = null; let data = await asyncFunction(); // 這裏用await關鍵字,就能拿到結果值;不然,沒有await的話,只能拿到Promise對象 if (array.length > 0) { // 這裏會拋出異常,下面的catch也能抓到 array.push(data); } } catch (error) { alert(JSON.stringify(error)) } }
6)await對於結果的處理: await是個運算符,用於組成表達式,await表達式的運算結果取決於它等的東西,若是它等到的不是一個Promise對象,那麼await表達式的運算結果就是它等到的東西;若是它等到的是一個Promise對象,await就忙起來了,它會阻塞其後面的代碼,等着Promise對象resolve,而後獲得resolve的值,做爲await表達式的運算結果;雖然是阻塞,但async函數調用並不會形成阻塞,它內部全部的阻塞都被封裝在一個Promise對象中異步執行,這也正是await必須用在async函數中的緣由;
5、套路分析一
// 異步過程封裝 function sleep(ms) { return new Promise((resolve) => { setTimeout(() => { resolve('sleep for ' + ms + ' ms'); }, ms); }); }
// 定義異步流程,能夠將按照須要定製,就像寫同步代碼那樣 async function asyncFunction() { console.time('asyncFunction total executing:'); const sleep1 = await sleep(2000); console.log('sleep1: ' + sleep1); const [sleep2, sleep3, sleep4]= await Promise.all([sleep(2000), sleep(1000), sleep(1500)]); console.log('sleep2: ' + sleep2); console.log('sleep3: ' + sleep3); console.log('sleep4: ' + sleep4); const sleepRace = await Promise.race([sleep(3000), sleep(1000), sleep(1000)]); console.log('sleep race: ' + sleepRace); console.timeEnd('asyncFunction total executing:'); return 'asyncFunction done.' // 這個能夠不返回,這裏只是作個標記,爲了顯示流程 }
// 像普通函數調用async函數,在then函數中獲取整個流程的返回信息,在catch函數統一處理出錯信息 asyncFunction().then(data => { console.log(data); // asyncFunction return 的內容在這裏獲取 }).catch(error => { console.log(error); // asyncFunction 的錯誤統一在這裏抓取 }); console.log('after asyncFunction code executing....'); // 這個表明asyncFunction函數後的代碼, // 顯示asyncFunction自己會當即返回,不會阻塞主線程
// 執行結果 after asyncFunction code executing.... sleep1: sleep for 2000 ms sleep2: sleep for 2000 ms sleep3: sleep for 1000 ms sleep4: sleep for 1500 ms sleep race: sleep for 1000 ms asyncFunction total executing:: 5006.276123046875ms asyncFunction done.
代碼分析
after asyncFunction code executing....代碼位置在async函數asyncFunction()調用以後,反而先輸出,這說明async函數asyncFunction()調用以後會立刻返回,不會阻塞主線程;
sleep1: sleep for 2000 ms這是第一個await以後的第一個異步過程,最早執行,也最早完成,說明後面的代碼,不管是同步和異步,都在等他執行完畢;
sleep2 ~ sleep4這是第二個await以後的Promise.all()異步過程,這是「比慢模式」,三個sleep都完成後,再運行下面的代碼,耗時最長的是2000ms;
sleep race: sleep for 1000 ms這是第三個await以後的Promise.race()異步過程,這是「比快模式」,耗時最短sleep都完成後,就運行下面的代碼,耗時最短的是1000ms;
asyncFunction total executing:: 5006.276123046875ms這是最後的統計總共運行時間代碼,三個await以後的異步過程之和:
1000(獨立的) + 2000(Promise.all) + 1000(Promise.race) = 5000ms
這個和統計出來的5006.276123046875ms很是接近,說明上面的異步過程,和同步代碼執行過程一致,協程真的是在等待異步過程執行完畢;
asyncFunction done.這個是async函數返回的信息,在執行時的then函數中得到,說明整個流程完畢以後參數傳遞的過程;
6、套路分析二
/** * 傳入參數 n,表示這個函數執行的時間(毫秒) * 執行的結果是 n + 200,這個值將用於下一步驟 */ 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}`); console.timeEnd("doIt"); }); } doIt(); // c:\var\test>node --harmony_async_await . // step1 with 300 // step2 with 500 // step3 with 700 // result is 900 // doIt: 1507.251ms
// 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}`); console.timeEnd("doIt"); } doIt();
7、套路分析三
function step1(n) { console.log(`step1 with ${n}`); return takeLongTime(n); } function step2(m, n) { console.log(`step2 with ${m} and ${n}`); return takeLongTime(m + n); } function step3(k, m, n) { console.log(`step3 with ${k}, ${m} and ${n}`); return takeLongTime(k + m + n); }
// Promise方式調用 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}`); console.timeEnd("doIt"); }); } 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}`); console.timeEnd("doIt"); } doIt(); // c:\var\test>node --harmony_async_await . // step1 with 300 // step2 with 800 = 300 + 500 // step3 with 1800 = 300 + 500 + 1000 // result is 2000 // doIt: 2907.387ms