最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解ES6之a(chǎn)sync+await 同步/異步方案

 更新時間:2017年09月19日 14:43:39   作者:Jeremy_young  
這篇文章主要介紹了詳解ES6之a(chǎn)sync+await 同步/異步方案,本文以最簡明的方式來疏通 async + await,有興趣的可以了解下

異步編程一直是JavaScript 編程的重大事項。關(guān)于異步方案, ES6 先是出現(xiàn)了 基于狀態(tài)管理的 Promise,然后出現(xiàn)了 Generator 函數(shù) + co 函數(shù),緊接著又出現(xiàn)了 ES7 的 async + await 方案。

本文力求以最簡明的方式來疏通 async + await。

異步編程的幾個場景

先從一個常見問題開始:一個for 循環(huán)中,如何異步的打印迭代順序?

我們很容易想到用閉包,或者 ES6 規(guī)定的 let 塊級作用域來回答這個問題。

for (let val of [1, 2, 3, 4]) {
  setTimeout(() => console.log(val),100);
}
// => 預(yù)期結(jié)果依次為:1, 2, 3, 4

這里描述的是一個均勻發(fā)生的的異步,它們被依次按既定的順序排在異步隊列中等待執(zhí)行。

如果異步不是均勻發(fā)生的,那么它們被注冊在異步隊列中的順序就是亂序的。

for (let val of [1, 2, 3, 4]) {
  setTimeout(() => console.log(val), 100 * Math.random());
}
// => 實際結(jié)果是隨機(jī)的,依次為:4, 2, 3, 1

返回的結(jié)果是亂序不可控的,這本來就是最為真實的異步。但另一種情況是,在循環(huán)中,如果希望前一個異步執(zhí)行完畢、后一個異步再執(zhí)行,該怎么辦?

for (let val of ['a', 'b', 'c', 'd']) {
  // a 執(zhí)行完后,進(jìn)入下一個循環(huán)
  // 執(zhí)行 b,依此類推
}

這不就是多個異步 “串行” 嗎!

在回調(diào) callback 嵌套異步操作、再回調(diào)的方式,不就解決了這個問題!或者,使用 Promise + then() 層層嵌套同樣也能解決問題。但是,如果硬是要將這種嵌套的方式寫在循環(huán)中,還恐怕還需費(fèi)一番周折。試問,有更好的辦法嗎?

異步同步化方案

試想,如果要去將一批數(shù)據(jù)發(fā)送到服務(wù)器,只有前一批發(fā)送成功(即服務(wù)器返回成功的響應(yīng)),才開始下一批數(shù)據(jù)的發(fā)送,否則終止發(fā)送。這就是一個典型的 “for 循環(huán)中存在相互依賴的異步操作” 的例子。

明顯,這種 “串行” 的異步,實質(zhì)上可以當(dāng)成同步。它和亂序的異步比較起來,花費(fèi)了更多的時間。按理說,我們希望程序異步執(zhí)行,就是為了 “跳過” 阻塞,較少時間花銷。但與之相反的是,如果需要一系列的異步 “串行”,我們應(yīng)該怎樣很好的進(jìn)行編程?

對于這個 “串行” 異步,有了 ES6 就非常容易的解決了這個問題。

async function task () {
  for (let val of [1, 2, 3, 4]) {
    // await 是要等待響應(yīng)的
    let result = await send(val);
    if (!result) {
      break;
    }
  }
}
task();

從字面上看,就是本次循環(huán),等有了結(jié)果,再進(jìn)行下一次循環(huán)。因此,循環(huán)每執(zhí)行一次就會被暫停(“卡住”)一次,直到循環(huán)結(jié)束。這種編碼實現(xiàn),很好的消除了層層嵌套的 “回調(diào)地獄” 問題,降低了認(rèn)知難度。

這就是異步問題同步化的方案。關(guān)于這個方案,如果說 Promise 主要解決的是異步回調(diào)問題,那么 async + await 主要解決的就是將異步問題同步化,降低異步編程的認(rèn)知負(fù)擔(dān)。

async + await “外異內(nèi)同”

早先接觸這套 API 時,看著繁瑣的文檔,一知半解的認(rèn)為 async + await 主要用來解決異步問題同步化的。

其實不然。從上面的例子看到:async 關(guān)鍵字聲明了一個 異步函數(shù),這個 異步函數(shù) 體內(nèi)有一行 await 語句,它告示了該行為同步執(zhí)行,并且與上下相鄰的代碼是依次逐行執(zhí)行的。

將這個形式化的東西再翻譯一下,就是:

1、async 函數(shù)執(zhí)行后,總是返回了一個 promise 對象
2、await 所在的那一行語句是同步的

其中,1 說明了從外部看,task 方法執(zhí)行后返回一個 Promise 對象,正因為它返回的是 Promise,所以可以理解task 是一個異步方法。毫無疑問它是這樣用的:

task().then((val) => {alert(val)})
   .then((val) => {alert(val)})

2 說明了在 task 函數(shù)內(nèi)部,異步已經(jīng)被 “削” 成了同步。整個就是一個執(zhí)行稍微耗時的函數(shù)而已。

綜合 1、2,從形式上看,就是 “task 整體是一個異步函數(shù),內(nèi)部整個是同步的”,簡稱“外異內(nèi)同”。

整體是一個異步函數(shù) 不難理解。在實現(xiàn)上,我們不妨逆向一下,語言層面讓async關(guān)鍵字調(diào)用時,在函數(shù)執(zhí)行的末尾強(qiáng)制增加一個promise 反回:

async fn () {
  let result;
  // ...
  //末尾返回 promise
  return isPromise(result)? 
      result : Promise.resolve(undefined);
}

內(nèi)部是同步的 是怎么做到的?實際上 await 調(diào)用,是讓后邊的語句(函數(shù))做了一個遞歸執(zhí)行,直到獲取到結(jié)果并使其 狀態(tài) 變更,才會 resolve 掉,而只有 resolve 掉,await 那一行代碼才算執(zhí)行完,才繼續(xù)往下一行執(zhí)行。所以,盡管外部是一個大大的 for 循環(huán),但是整個 for 循環(huán)是依次串行的。

因此,僅從上述框架的外觀出發(fā),就不難理解 async + await 的意義。使用起來也就這么簡單,反而 Promise 是一個必須掌握的基礎(chǔ)件。

秉承本次《重讀 ES6》系列的原則,不過多追求理解細(xì)節(jié)和具體實現(xiàn)過程。我們繼續(xù)鞏固一下這個 “形式化” 的理解。

async + await 的進(jìn)一步理解

有這樣的一個異步操作 longTimeTask,已經(jīng)用 Promise 進(jìn)行了包裝。借助該函數(shù)進(jìn)行一系列驗證。

const longTimeTask = function (time) {
 return new Promise((resolve, reject) => {
  setTimeout(()=>{
   console.log(`等了 ${time||'xx'} 年,終于回信了`);
   resolve({'msg': 'task done'});
  }, time||1000)
 })
}

async 函數(shù)的執(zhí)行情況

如果,想查看 async exec1 函數(shù)的返回結(jié)果,以及 await 命令的執(zhí)行結(jié)果:

const exec1 = async function () {
 let result = await longTimeTask();
 console.log('result after long time ===>', result);
}
// 查看函數(shù)內(nèi)部執(zhí)行順序
exec1();
// => 等了 xx 年,終于回信了
// => result after long time ===> Object {msg: "task done"}

//查看函數(shù)總體返回值
console.log(exec1());
// => Promise {[[PromiseStatus]]: "pending",...}
// => 同上

以上 2 步執(zhí)行,清晰的證明了 exec1 函數(shù)體內(nèi)是同步、逐行逐行執(zhí)行的,即先執(zhí)行完異步操作,然后進(jìn)行 console.log() 打印。而 exec1() 的執(zhí)行結(jié)果就直接是一個 Promise,因為它最先會蹦出來一串 Promise ...,然后才是 exec1 函數(shù)的內(nèi)部執(zhí)行日志。

因此,所有驗證,完全符合 整體是一個異步函數(shù),內(nèi)部整個是同步的 的總結(jié)。

await 如何執(zhí)行其后語句?

回到 await ,看看它是如何執(zhí)行其后邊的語句的。假設(shè):讓 longTimeTask() 后邊直接帶 then() 回調(diào),分兩種情況:

1)then() 中不再返回任何東西
2) then() 中繼續(xù)手動返回另一個 promise

const exec2 = async function () {
 let result = await longTimeTask().then((res) => {
  console.log('then ===>', res.msg);
  res.msg = `${res.msg} then refrash message`;
  // 注釋掉這條 return 或 手動返回一個 promise
  return Promise.resolve(res);
 });
 console.log('result after await ===>', result.msg);
}
exec2();
// => 情況一 TypeError: Cannot read property 'msg' of undefined
// => 情況二 正常

首先,longTimeTask() 加上再多得 then() 回調(diào),也不過是放在了它的回調(diào)列隊 queue 里了。也就是說,await 命令之后始終是一條 表達(dá)式語句,只不過上述代碼書寫方式比較讓人迷惑。(比較好的實踐建議是,將 longTimeTask 方法身后的 then() 移入 longTimeTask 函數(shù)體封裝起來)

其次,手動返回另一個 promise 和什么也不返回,關(guān)系到 longTimeTask() 方法最終 resolve 出去的內(nèi)容不一樣。換句話說,await 命令會提取其后邊的promise 的 resolve 結(jié)果,進(jìn)而直接導(dǎo)致 result 的不同。

值得強(qiáng)調(diào)的是,await 命令只認(rèn) resolve 結(jié)果,對 reject 結(jié)果報錯。不妨用以下的 return 語句替換上述 return 進(jìn)行驗證。

return Promise.reject(res);

最后

其實,關(guān)于異步編程還有很多可以梳理的,比如跨模塊的異步編程、異步的單元測試、異步的錯誤處理以及什么是好的實踐。All in all, 限于篇幅,不在此匯總了。最后,async + await 確實是一個很優(yōu)雅的方案。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • JS判斷用戶用的哪個瀏覽器實例詳解

    JS判斷用戶用的哪個瀏覽器實例詳解

    這篇文章主要介紹了JS判斷用戶用的哪個瀏覽器的實例代碼,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-10-10
  • 基于Proxy的小程序狀態(tài)管理實現(xiàn)

    基于Proxy的小程序狀態(tài)管理實現(xiàn)

    這篇文章主要介紹了基于Proxy的小程序狀態(tài)管理實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-06-06
  • three.js創(chuàng)造時空裂縫特效實現(xiàn)示例

    three.js創(chuàng)造時空裂縫特效實現(xiàn)示例

    這篇文章主要為大家介紹了three.js創(chuàng)造時空裂縫特效實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • 不同Jquery版本引發(fā)的問題解決

    不同Jquery版本引發(fā)的問題解決

    用JS實現(xiàn)了一個拖拽排序,可因Jquery版本不同導(dǎo)致瀏覽器訪問時存在很多的問題,下面為大家介紹下具體的解決方法,感興趣的朋友可以參考下
    2013-10-10
  • 小白談?wù)剬S原型鏈的理解

    小白談?wù)剬S原型鏈的理解

    這篇文章主要介紹了小白談?wù)剬S原型鏈的理解的相關(guān)資料,需要的朋友可以參考下
    2016-05-05
  • Web表單提交之disabled問題js解決方法

    Web表單提交之disabled問題js解決方法

    這篇文章主要介紹了Web表單提交之disabled問題js解決方法,分析了通過js解決保存值也能保留用戶不能輸入的功能,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-01-01
  • JS實現(xiàn)頁面導(dǎo)航與內(nèi)容相互錨定實例詳解

    JS實現(xiàn)頁面導(dǎo)航與內(nèi)容相互錨定實例詳解

    這篇文章主要為大家介紹了JS實現(xiàn)頁面導(dǎo)航與內(nèi)容相互錨定實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • JavaScript獲取表單enctype屬性的方法

    JavaScript獲取表單enctype屬性的方法

    這篇文章主要介紹了JavaScript獲取表單enctype屬性的方法,涉及javascript操作表單屬性的技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-04-04
  • JS+CSS實現(xiàn)可以凹陷顯示選中單元格的方法

    JS+CSS實現(xiàn)可以凹陷顯示選中單元格的方法

    這篇文章主要介紹了JS+CSS實現(xiàn)可以凹陷顯示選中單元格的方法,實例分析了javascript操作css的技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-03-03
  • JS庫之ParticlesJS使用簡介

    JS庫之ParticlesJS使用簡介

    ParticlesJS是一個輕量級的創(chuàng)建粒子背景的 JavaScript 庫,接下來通過本文給大家分享JS庫之ParticlesJS使用簡介,感興趣的朋友一起看看吧
    2017-09-09

最新評論

崇州市| 临漳县| 阳曲县| 高邑县| 拜泉县| 柳林县| 东海县| 南岸区| 沐川县| 麦盖提县| 乌兰察布市| 乌兰浩特市| 台东县| 苏州市| 大同市| 中山市| 哈尔滨市| 绥芬河市| 鞍山市| 贡嘎县| 辰溪县| 东宁县| 房山区| 黄冈市| 和林格尔县| 克拉玛依市| 子长县| 怀远县| 邛崃市| 准格尔旗| 湘乡市| 西和县| 淄博市| 上栗县| 额敏县| 邻水| 嫩江县| 林周县| 应用必备| 南雄市| 牟定县|