摘要:基本用法函數(shù)返回一個對象,可以使用方法添加回調函數(shù)。拋出的錯誤對象會被方法回調函數(shù)接收到。也就是說,只有函數(shù)內部的異步操作執(zhí)行完,才會執(zhí)行方法指定的回調函數(shù)。命令后面的對象如果變?yōu)闋顟B(tài),則的參數(shù)會被方法的回調函數(shù)接收到。
含義
async函數(shù)是什么?一句話,它就是Generator函數(shù)的語法糖。
const fs = require("fs") const readFile = function(fileName){ return new Promise(function(resolve,reject){ fs.readFile(fileName,function(error,data){ if(error) return reject(error); resolve(data); }) }) } const gen = function*(){ const f1 = yield readFile("/etc/fstab"); const f2 = yield readFile("/etc/shells"); console.log(f1.toString()); console.log(f2.toString()); } //上面代碼的函數(shù)gen可以寫成async函數(shù),就是下面這樣。 const asyncReadFile = async function(){ const f1 = await readFile("/etc/fstab"); cosnt f2 = await readFile("/etc/shells"); console.log(f1.toString()); console.log(f2.toString()); }
一比較就會發(fā)現(xiàn),async函數(shù)就是將Generator函數(shù)的星號替換成async,將yield替換成await,僅此而已。
async函數(shù)對Generator函數(shù)的改進,體現(xiàn)在以下四點。
(1)內置執(zhí)行器
Generator函數(shù)的執(zhí)行必須靠執(zhí)行器,所以才有了co模塊,而async函數(shù)自帶執(zhí)行器。也就是說,async函數(shù)的執(zhí)行,與普通函數(shù)一模一樣,只要一行。
asyncReadFile()
上面的代碼調用了asyncReadFile函數(shù),然后它就會自動執(zhí)行,輸出最后結果。這完全不像Generator函數(shù),需要調用next方法,或則用co模塊,才能真正執(zhí)行,得到最后結果。
(2)更好的語義
async和await,比起星號和yield,語義更清楚了。async表示函數(shù)里有異步操作,await表示緊跟在后面的表達式需要等待結果。
(3)更廣的適用性
co模塊約定,yield命令后面只能是Thunk函數(shù)或Promise對象,而async函數(shù)的await命令后面,可以是Promise對象和原始類型的值(數(shù)值、字符串和布爾值,但這時等同于同步操作)。
(4)返回值是Promise
async函數(shù)的返回值是Promise對象,這比Generator函數(shù)的返回值Iterator對象方便多了。你可以用then方法指定下一步的操作。
進一步說,async函數(shù)完全可以看作多個異步操作,包裝成的一個Promise對象,而await命令就是內部then命令的語法糖。
基本用法
async函數(shù)返回一個Promise對象,可以使用then方法添加回調函數(shù)。當函數(shù)執(zhí)行的時候,一旦遇到await就會先返回,等到異步操作完成,再接著執(zhí)行函數(shù)體內后面的語句。
下面是一個例子。
async function getStockPriceByName(name){ const symbol = await getStockSymbol(name); const stockPrice = await getStockPrice(symbol); return stockPrice; } getStockPriceByName("goog").then(function(result){ console.log(result); })
上面代碼是一個獲取股票報價的函數(shù),函數(shù)前面的async關鍵字,表明該函數(shù)內部有異步操作。調用該函數(shù)時,會立即返回一個Promise對象。
下面是另一個例子,指定多少毫秒后輸出一個值。
function timeout(ms){ return new Promise((resolve)=>{ setTimeout(resolve,ms); }) } async function asyncPrint(value,ms){ await timeout(ms); console.log(value) } asyncPrint("hello world",50);
由于async函數(shù)返回的是Promise對象,可以作為await命令的參數(shù)。所以,上面的例子也可以寫成下面的形式。
async function timeout(ms){ await new Promise((resolve)=>{ setTimeout(resolve,ms); }) } async function asyncPrint(value, ms) { await timeout(ms); console.log(value); } asyncPrint("hello world", 5000);
async函數(shù)有多種使用形式。
//函數(shù)聲明 async function foo(){} //函數(shù)表達式 const foo = async function(){}; //對象的方法 let obj = {async foo(){}}; obj.foo().then(...) //Class的方法 class Storage{ constructor(){ this.cachePromise = caches.open("avatars"); } async getAvatar(name){ const cache = await this.cachePromise; return cache.match(`/avatars/${name}.jpg`); } } const storage = new Storage(); storage.getAvatar("jake").then(...); //箭頭函數(shù) const foo = async()=>{};返回Promise對象
async函數(shù)返回一個Promise對象。
async函數(shù)內部return語句返回的值,會成為then方法回調函數(shù)的參數(shù)。
async function f(){ return "hello world"; } f().then(v=>console.log(v))//"hello world"
上面代碼中,函數(shù)f內部return命令返回的值,會被then方法回調函數(shù)接收到。
async函數(shù)內部拋出錯誤,會導致返回Promise對象變?yōu)閞eject狀態(tài)。拋出的錯誤對象會被catch方法回調函數(shù)接收到。
async function f(){ throw new Error("出錯了"); } f().then( v=>console.log(v), e=>console.log(e) ) //Error;出錯了Promise對象的狀態(tài)變化
async函數(shù)返回的Promise對象,必須等到內部所有await命令后面的Promise對象執(zhí)行完,才會發(fā)生狀態(tài)改變,除非遇到return語句或則拋出錯誤。也就是說,只有async函數(shù)內部的異步操作執(zhí)行完,才會執(zhí)行then方法指定的回調函數(shù)。
async function getTitle(url) { let response = await fetch(url); let html = await response.text(); return html.match(/([sS]+) /i)[1]; } getTitle("https://tc39.github.io/ecma262/").then(console.log)
上面代碼中,函數(shù)getTitle內部有三個操作:抓取網(wǎng)頁、取出文本、匹配頁面標題。只有這三個操作全部完成,才會執(zhí)行then方法里面的console.log()
await命令
正常情況下,await命令后面是一個Promise對象,返回該對象的結果。如果不是Promsie對象,就直接返回對應的值。
async function f(){ //等同于123; return await 123; } f().then(v=>console.log(v))
上面代碼中,await命令的參數(shù)是數(shù)值123,這時等同于return 123.
另一種情況是,await命令后面是一個thenable對象(即定義then方法的對象),那么await會將其等同于Promise對象。
class Sleep{ constructor(timeout){ this.timeout = timeout; } then(resolve,reject){ const startTime = Date.now(); setTimeout( ()=>resolve(Date.now()-startTime) this.timeout ) } } (async()=>{ const actualTime = await new Sleep(1000); console.log(actualTime); })();
上面代碼中,await命令后面是一個Sleep對象的實例。這個實例不是Promise對象,但是因為定義了then方法,await會將其視為Promise處理。
await命令后面的Promise對象如果變?yōu)閞eject狀態(tài),則reject的參數(shù)會被catch方法的回調函數(shù)接收到。
async function f(){ await Promise.reject("出錯了"); } f().then(v=>console.log(v)).catch(e=>console.log(e));
注意,上面代碼中,await語句前面沒有return,但是reject方法的參數(shù)依然傳入到catch方法的回調函數(shù)。這里如果是await前面加上return,效果是一樣的。
任何一個await語句后面的Promise對象變?yōu)閞eject狀態(tài),那么整個async函數(shù)都會中斷執(zhí)行。
有時,我們希望即使前一個異步操作失敗,也不要中斷后面的異步操作。這時可以將第一個await放在try...catch結構里面,這樣不管這個異步操作是否成功,第二個await都會執(zhí)行。
async function f(){ try{ await Promise.reject("出錯了"); }catch(e){ } return await Promise.resolve("hello world"); } f().then(v=>console.log(v))//hello world
另一種方法是await后面的Promise對象再跟一個catch方法,處理前面可能出現(xiàn)的錯誤。
async function f(){ await Promise.reject("出錯了") .catch(e=>console.log(e)); return await Promise.resolve("hello world"); } f().then(v=>console.log(v)) //出錯了 //hello world
錯誤處理
如果await后面的異步操作出錯,那么等同于async函數(shù)返回的Promise對象被reject。防止出錯的方法,也是將其放在try...catch代碼塊之中。
async function f(){ try{ await new Promise(function(resolve,reject){ throw new Error("出錯了") }); }catch(e){} return await("hello world"); }
如果有多個await命令,可以統(tǒng)一放在try...catch結構中。
async function main(){ try{ const val1 = await firstStep(); const val2 = await secondStep(val1); const val3 = await thirdStep(val1,val2); console.log("Final",val3); } catch(err){ console.error(err); } }
下面的例子使用try...catch結構,實現(xiàn)多次重復嘗試。
const superagent = require("superagent"); const NUM_RETRIES = 3; async function test(){ let i; for(i=0;i上面代碼中,如果await操作成功,就會使用break語句退出循環(huán);如果失敗,會被catch語句捕捉,然后進入下一輪循環(huán)。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/100281.html
摘要:版本以及之前,本身還沒有異步執(zhí)行代碼的能力,宿主環(huán)境傳遞給引擎,然后按順序執(zhí)行,由宿主發(fā)起任務。采納引擎術語,把宿主發(fā)起的任務稱為宏觀任務,把引擎發(fā)起的任務稱為微觀任務?;居梅ㄊ纠幕卣{是一個異步的執(zhí)行過程。 筆記說明 重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,每天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些要點筆記以及感悟,完整的...
摘要:版本以及之前,本身還沒有異步執(zhí)行代碼的能力,宿主環(huán)境傳遞給引擎,然后按順序執(zhí)行,由宿主發(fā)起任務。采納引擎術語,把宿主發(fā)起的任務稱為宏觀任務,把引擎發(fā)起的任務稱為微觀任務?;居梅ㄊ纠幕卣{是一個異步的執(zhí)行過程。 筆記說明 重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,每天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些要點筆記以及感悟,完整的...
摘要:版本以及之前,本身還沒有異步執(zhí)行代碼的能力,宿主環(huán)境傳遞給引擎,然后按順序執(zhí)行,由宿主發(fā)起任務。采納引擎術語,把宿主發(fā)起的任務稱為宏觀任務,把引擎發(fā)起的任務稱為微觀任務?;居梅ㄊ纠幕卣{是一個異步的執(zhí)行過程。 筆記說明 重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,每天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些要點筆記以及感悟,完整的...
摘要:方法在中定義的函數(shù)。這種聲明方式會定義一個生成器函數(shù),它返回一個對象。類用定義的類,實際上也是函數(shù)。調用函數(shù)時使用的引用,決定了函數(shù)執(zhí)行時刻的值。表示當為時,取全局對象,對應了普通函數(shù)。四操作的內置函數(shù)和可以指定函數(shù)調用時傳入的值。 筆記說明 重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,每天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些要...
閱讀 509·2021-09-03 00:22
閱讀 1382·2021-08-03 14:03
閱讀 2102·2021-07-25 21:37
閱讀 661·2019-08-30 13:18
閱讀 1889·2019-08-29 16:19
閱讀 2697·2019-08-29 13:22
閱讀 1321·2019-08-29 12:16
閱讀 2597·2019-08-26 12:16