async/await 真不是你想象中那麼簡單

先上代碼promise

公共代碼

function getData(data, time) {
                return new Promise(function (resolve, reject) {
                    setTimeout(function () {
                        resolve(data);
                    }, time)
                })
            }
            let results = [];
            let startTime = new Date();
            laucher();

代碼段一

async function laucher() {
                let dataA = await getData('a', 2000);
                results.push(`${dataA}在${new Date() - startTime}毫秒放入`);
                let dataB = await getData('b', 3000);
                results.push(`${dataB}在${new Date() - startTime}毫秒放入`);
                let dataC = await getData('c', 1000);
                results.push(`${dataC}在${new Date() - startTime}毫秒放入`);
                console.log(results, `輸出時間${new Date() - startTime}毫秒`);
            }

輸出併發

["a在2002毫秒放入", "b在5004毫秒放入", "c在6006毫秒放入"] "輸出時間6006毫秒"

代碼段二

async function laucher() {
              let dataAPromise = getData('a', 2000);
              let dataBPromise = getData('b', 3000);
              let dataCPromise = getData('c', 1000);
              let promises = [dataAPromise, dataBPromise, dataCPromise];
              results = await Promise.all(promises);
              console.log(results, `輸出時間${new Date() - startTime}毫秒`);
            }

輸出異步

["a", "b", "c"] "輸出時間3006毫秒"

代碼段三

async function laucher() {
              let dataAPromise = getData('a', 2000);
              let dataBPromise = getData('b', 3000);
              let dataCPromise = getData('c', 1000);
              dataA = await dataAPromise;
              results.push(`${dataA}在${new Date() - startTime}毫秒放入`);
              dataB = await dataBPromise;
              results.push(`${dataB}在${new Date() - startTime}毫秒放入`);
              dataC = await dataCPromise;
              results.push(`${dataC}在${new Date() - startTime}毫秒放入`);
              console.log(results, `輸出時間${new Date() - startTime}毫秒`);
            }

輸出async

["a在2003毫秒放入", "b在3001毫秒放入", "c在3001毫秒放入"] "輸出時間3002毫秒"

代碼段四

async function laucher() {
              let dataAPromise = getData('a', 2000);
              let dataBPromise = getData('b', 3000);
              let dataCPromise = getData('c', 1000);
              (async () => {
                dataA = await dataAPromise;
                results.push(`${dataA}在${new Date() - startTime}毫秒放入`);
              })();
              (async () => {
                dataB = await dataBPromise;
                results.push(`${dataB}在${new Date() - startTime}毫秒放入`);
                console.log(results, `輸出時間${new Date() - startTime}毫秒`);//results放在最後返回的請求中
              })();
              (async () => {
                dataC = await dataCPromise;
                results.push(`${dataC}在${new Date() - startTime}毫秒放入`);
              })();
            }

輸出函數

["c在1002毫秒放入", "a在2002毫秒放入", "b在3003毫秒放入"] "輸出時間3003毫秒"

總結

使用setTimeout模擬了3條異步請求,分別2000,3000,1000毫秒後返回'a', 'b', 'c',
第一種方法很好理解,就是一步一步執行,這種方法適合請求參數依賴上一個請求返回值的狀況,在這裏是不存在這種關係的,也就是這種方法在這裏效率是比較低的。code

第二種方法一開始就發起了3個請求,並等待3請求都到達後獲取數據。get

第三種方法也一開始就發起了3個請求,並在請求到達後依次執行,由於a請求到達時間是2秒,a請求到達後,把a的結果推入results,再往下執行,b請求是3秒,b請求遲a請求一秒到達,也就是再過一秒後把b的結果推入results,,c的請求是1秒,這個時候c早已到達,在這輪循環末尾能夠當即把c推入。a請求返回的數據2秒後就能操做了,這種方法比第二種方法能夠更快處理數據。若是請求時間是依次遞減的,那麼和方法二效果是同樣,在有多個請求時這種狀況通常不存在。it

第四種方法和第三種方法的區別是最早到達的請求最快放入結果集,也就是我不用排隊等待處理,哪一個數據先返回我就先處理哪一個數據,假如c請求返回的數據須要花比較長的時間處理,我在一秒後就能開始處理了,可是第三種方法我得3秒後才能開始處理。能夠看到我把results的輸出放在了b請求到達的函數中,由於results在最後一個請求到達後才能完整輸出,與方法三的區別是獲取結果的操做也是異步的,這個很關鍵,也是和方法三最大的區別,經過在外層包裝一個自執行函數,能夠防止await的操做權跳出laucher外部,從而併發發起3個獲取結果的操做。可能你們會有疑問假如我不清楚哪一個請求最後到達,那怎麼獲取最後的results值,這種狀況能夠在外面包一個Promise.all,能夠仔細看一下下面兩個函數的區別io

async function laucher() {
              let dataAPromise = getData('a', 2000);
              let dataBPromise = getData('b', 3000);
              let dataCPromise = getData('c', 1000);
              let promises = [dataAPromise, dataBPromise, dataCPromise];
              results = await Promise.all(promises);
              console.log(results, `輸出時間${new Date() - startTime}毫秒`);
            }

輸出console

["a", "b", "c"] "輸出時間3003毫秒"
async function laucher() {
              let dataAPromise = getData('a', 2000);
              let dataBPromise = getData('b', 3000);
              let dataCPromise = getData('c', 1000);
              let promises = [dataAPromise, dataBPromise, dataCPromise];
              results = await Promise.all(promises.map(async function (promise) {
                let data = await promise;
                console.log(`${data}在${new Date() - startTime}毫秒輸出`);
                //這裏能夠提早處理數據
                return data
              }));
              console.log(results);
            }

輸出

c在1002毫秒輸出
a在2003毫秒輸出
b在3003毫秒輸出
["a", "b", "c"] "輸出時間3004毫秒"

若是請求之間不存在繼發關係,而且請求到達後要執行一些運算,那麼按效率來講
方法4 > 方法3 > 方法2 > 方法1
每種方法都對應一種加載的策略,以一個使用場景來講明一下,向後臺加載頁面組件(假設組件個數是3)
執行流程:

方法一: 發起組件1的請求 -> 組件1數據到達後渲染組件1 -> 發起組件2的請求 -> 組件2數據到達後渲染組件2 -> 發起組件3的請求 -> 組件3數據到達後渲染組件3

方法二: 同時發起組件1,2,3的請求 -> 組件1,2,3的數據都到達後渲染組件1,2,3

方法三: 同時發起組件1,2,3的請求 -> 組件1數據到達後渲染組件1 -> 組件2數據到達後渲染組件2 -> 組件3數據到達後渲染組件3

方法四: 同時發起組件1,2,3的請求 -> 最快到達的組件數據到達後渲染最快到達的組件 -> 第二快到達的組件數據到達後渲染第二快到達的組件 -> 最慢到達的組件數據到達後渲染最慢到達的組件

針對以上場景能夠看出方法四可讓咱們的頁面最快渲染出內容

最後

能夠看出雖然引入async/await,可讓你的代碼很精簡,可是async/await自己的執行流程倒是很複雜的,下面的代碼你能夠猜一下輸出結果(補充一點:有的文章會指出forEach函數不能放入異步操做,這種結論是錯誤的,若是是繼發關係確實不適宜用forEach,但不能表示不能放入異步操做,反而這種操做能提升獲取數據的效率)

[1, 2].forEach(async function (value) {
              console.log(value);
              await console.log('a');
              console.log('c');
              await console.log('d');
              console.log('e');
            })
            console.log(3);
相關文章
相關標籤/搜索