国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

如何更好的編寫async函數

isaced / 481人閱讀

摘要:一個簡單的轉換為的例子我們調用函數返回一個的實例,在實例化的過程中進行文件的讀取,當文件讀取的回調觸發式,進行狀態的變更,或者狀態的變更我們使用來監聽,第一個回調為的處理,第二個回調為的處理。

2018年已經到了5月份,node4.x版本也已經停止了維護  
我司的某個服務也已經切到了8.x,目前正在做koa2.x的遷移
將之前的generator全部替換為async
但是,在替換的過程中,發現一些濫用async導致的時間上的浪費
所以來談一下,如何優化async代碼,更充分的利用異步事件流 杜絕濫用async
首先,你需要了解Promise

Promise是使用async/await的基礎,所以你一定要先了解Promise是做什么的
Promise是幫助解決回調地獄的一個好東西,能夠讓異步流程變得更清晰。
一個簡單的Error-first-callback轉換為Promise的例子:

const fs = require("fs")

function readFile (fileName) {
  return new Promise((resolve, reject) => {
    fs.readFile(fileName, (err, data) => {
      if (err) reject(err)

      resolve(data)
    })
  })
}

readFile("test.log").then(data => {
  console.log("get data")
}, err => {
  console.error(err)
})

我們調用函數返回一個Promise的實例,在實例化的過程中進行文件的讀取,當文件讀取的回調觸發式,進行Promise狀態的變更,resolved或者rejected
狀態的變更我們使用then來監聽,第一個回調為resolve的處理,第二個回調為reject的處理。

async與Promise的關系

async函數相當于一個簡寫的返回Promise實例的函數,效果如下:

function getNumber () {
  return new Promise((resolve, reject) => {
    resolve(1)
  })
}
// =>
async function getNumber () {
  return 1
}

兩者在使用上方式上完全一樣,都可以在調用getNumber函數后使用then進行監聽返回值。
以及與async對應的await語法的使用方式:

getNumber().then(data => {
  // got data
})
// =>
let data = await getNumber()

await的執行會獲取表達式后邊的Promise執行結果,相當于我們調用then獲取回調結果一樣。
P.S. 在async/await支持度還不是很高的時候,大家都會選擇使用generator/yield結合著一些類似于co的庫來實現類似的效果

async函數代碼執行是同步的,結果返回是異步的

async函數總是會返回一個Promise的實例 這點兒很重要
所以說調用一個async函數時,可以理解為里邊的代碼都是處于new Promise中,所以是同步執行的
而最后return的操作,則相當于在Promise中調用resolve

async function getNumber () {
  console.log("call getNumber()")

  return 1
}

getNumber().then(_ => console.log("resolved"))
console.log("done")

// 輸出順序:
// call getNumber()
// done
// resolved
Promise內部的Promise會被消化

也就是說,如果我們有如下的代碼:

function getNumber () {
  return new Promise(resolve => {
    resolve(Promise.resolve(1))
  })
}

getNumber().then(data => console.log(data)) // 1

如果按照上邊說的話,我們在then里邊獲取到的data應該是傳入resolve中的值 ,也就是另一個Promise的實例。
但實際上,我們會直接獲得返回值:1,也就是說,如果在Promise中返回一個Promise,實際上程序會幫我們執行這個Promise,并在內部的Promise狀態改變時觸發then之類的回調。
一個有意思的事情:

function getNumber () {
  return new Promise(resolve => {
    resolve(Promise.reject(new Error("Test")))
  })
}

getNumber().catch(err => console.error(err)) // Error: Test

如果我們在resolve中傳入了一個reject,則我們在外部則可以直接使用catch監聽到。
這種方式經常用于在async函數中拋出異常
如何在async函數中拋出異常:

async function getNumber () {
  return Promise.reject(new Error("Test"))
}
try {
  let number = await getNumber()
} catch (e) {
  console.error(e)
}
一定不要忘了await關鍵字

如果忘記添加await關鍵字,代碼層面并不會報錯,但是我們接收到的返回值卻是一個Promise

let number = getNumber()
console.log(number) // Promise

所以在使用時一定要切記await關鍵字

let number = await getNumber()
console.log(number) // 1
不是所有的地方都需要添加await

在代碼的執行過程中,有時候,并不是所有的異步都要添加await的。
比如下邊的對文件的操作:
我們假設fs所有的API都被我們轉換為了Promise版本

async function writeFile () {
  let fd = await fs.open("test.log")
  fs.write(fd, "hello")
  fs.write(fd, "world")
  return fs.close(fd)
}

就像上邊說的,Promise內部的Promise會被消化,所以我們在最后的close也沒有使用await
我們通過await打開一個文件,然后進行兩次文件的寫入。
但是注意了,在兩次文件的寫入操作前邊,我們并沒有添加await關鍵字。
因為這是多余的,我們只需要通知API,我要往這個文件里邊寫入一行文本,順序自然會由fs來控制 。
最后再進行close,因為如果我們上邊在執行寫入的過程還沒有完成時,close的回調是不會觸發的,
也就是說,回調的觸發就意味著上邊兩步的write已經執行完成了。

合并多個不相干的async函數調用

如果我們現在要獲取一個用戶的頭像和用戶的詳細信息(而這是兩個接口 雖說一般情況下不太會出現

async function getUser () {
  let avatar = await getAvatar()
  let userInfo = await getUserInfo()

  return {
    avatar,
    userInfo
  }
}

這樣的代碼就造成了一個問題,我們獲取用戶信息的接口并不依賴于頭像接口的返回值。
但是這樣的代碼卻會在獲取到頭像以后才會去發送獲取用戶信息的請求。
所以我們對這種代碼可以這樣處理:

async function getUser () {
  let [avatar, userInfo] = await Promise.all([getAvatar(), getUserInfo()])

  return {
    avatar,
    userInfo
  }
}

這樣的修改就會讓getAvatargetUserInfo內部的代碼同時執行,同時發送兩個請求,在外層通過包一層Promise.all來確保兩者都返回結果。

讓相互沒有依賴關系的異步函數同時執行

一些循環中的注意事項 forEach

當我們調用這樣的代碼時:

async function getUsersInfo () {
  [1, 2, 3].forEach(async uid => {
    console.log(await getUserInfo(uid))
  })
}

function getuserInfo (uid) {
  return new Promise(resolve => {
    setTimeout(_ => resolve(uid), 1000)
  })
}

await getUsersInfo()

這樣的執行好像并沒有什么問題,我們也會得到123三條log的輸出,
但是當我們在await getUsersInfo()下邊再添加一條console.log("done")的話,就會發現:
我們會先得到done,然后才是三條uidlog,也就是說,getUsersInfo返回結果時,其實內部Promise并沒有執行完。
這是因為forEach并不會關心回調函數的返回值是什么,它只是運行回調。

不要在普通的for、while循環中使用await

使用普通的forwhile循環會導致程序變為串行:

for (let uid of [1, 2, 3]) {
  let result = await getUserInfo(uid)
}

這樣的代碼運行,會在拿到uid: 1的數據后才會去請求uid: 2的數據

關于這兩種問題的解決方案:

目前最優的就是將其替換為map結合著Promise.all來實現:

await Promise.all([1, 2, 3].map(async uid => await getUserInfo(uid)))

這樣的代碼實現會同時實例化三個Promise,并請求getUserInfo

P.S. 草案中有一個await*,可以省去Promise.all
await* [1, 2, 3].map(async uid => await getUserInfo(uid))
P.S. 為什么在使用Generator+co時沒有這個問題

在使用koa1.x的時候,我們直接寫yield [].map是不會出現上述所說的串行問題的
看過co源碼的小伙伴應該都明白,里邊有這么兩個函數(刪除了其余不相關的代碼):

function toPromise(obj) {
  if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
  return obj;
}

function arrayToPromise(obj) {
  return Promise.all(obj.map(toPromise, this));
}

co是幫助我們添加了Promise.all的處理的(膜拜TJ大佬)。

總結

總結一下關于async函數編寫的幾個小提示:

使用return Promise.reject()async函數中拋出異常

讓相互之間沒有依賴關系的異步函數同時執行

不要在循環的回調中/forwhile循環中使用await,用map來代替它

參考資料

async-function-tips

本人GitHub: jiasm 歡迎小伙伴們follow、交流

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/94941.html

相關文章

  • async/await 更好異步解決方案

    摘要:大家都一直在嘗試使用更好的方案來解決這些問題。這是一個用同步的思維來解決異步問題的方案。當我們發出了請求,并不會等待響應結果,而是會繼續執行后面的代碼,響應結果的處理在之后的事件循環中解決。我們可以用一個兩人問答的場景來比喻異步與同步。 在實際開發中總會遇到許多異步的問題,最常見的場景便是接口請求之后一定要等一段時間才能得到結果,如果遇到多個接口前后依賴,那么問題就變得復雜。大家都一直...

    Ali_ 評論0 收藏0
  • 【面試篇】寒冬求職季之你必須要懂原生JS(中)

    摘要:如果你還沒讀過上篇上篇和中篇并無依賴關系,您可以讀過本文之后再閱讀上篇,可戳面試篇寒冬求職季之你必須要懂的原生上小姐姐花了近百個小時才完成這篇文章,篇幅較長,希望大家閱讀時多花點耐心,力求真正的掌握相關知識點。 互聯網寒冬之際,各大公司都縮減了HC,甚至是采取了裁員措施,在這樣的大環境之下,想要獲得一份更好的工作,必然需要付出更多的努力。 一年前,也許你搞清楚閉包,this,原型鏈,就能獲得...

    andycall 評論0 收藏0
  • JavaScript是如何工作:事件循環和異步編程崛起+ 5種使用 async/await 更

    摘要:事件循環從回調隊列中獲取并將其推入調用堆棧。執行從調用堆棧中移除從調用堆棧中移除快速回顧值得注意的是,指定了事件循環應該如何工作,這意味著在技術上它屬于引擎的職責范圍,不再僅僅扮演宿主環境的角色。 此篇是 JavaScript是如何工作的第四篇,其它三篇可以看這里: JavaScript是如何工作的:引擎,運行時和調用堆棧的概述! JavaScript是如何工作的:深入V8引擎&編寫...

    Honwhy 評論0 收藏0
  • 【全文】狼叔:如何正確學習Node.js

    摘要:感謝大神的免費的計算機編程類中文書籍收錄并推薦地址,以后在倉庫里更新地址,聲音版全文狼叔如何正確的學習簡介現在,越來越多的科技公司和開發者開始使用開發各種應用。 說明 2017-12-14 我發了一篇文章《沒用過Node.js,就別瞎逼逼》是因為有人在知乎上黑Node.js。那篇文章的反響還是相當不錯的,甚至連著名的hax賀老都很認同,下班時讀那篇文章,竟然坐車的還坐過站了。大家可以很...

    Edison 評論0 收藏0
  • 【全文】狼叔:如何正確學習Node.js

    摘要:感謝大神的免費的計算機編程類中文書籍收錄并推薦地址,以后在倉庫里更新地址,聲音版全文狼叔如何正確的學習簡介現在,越來越多的科技公司和開發者開始使用開發各種應用。 說明 2017-12-14 我發了一篇文章《沒用過Node.js,就別瞎逼逼》是因為有人在知乎上黑Node.js。那篇文章的反響還是相當不錯的,甚至連著名的hax賀老都很認同,下班時讀那篇文章,竟然坐車的還坐過站了。大家可以很...

    fengxiuping 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<