Async Await
接著上一篇Generator co的使用 https://juejin.im/post/5ab51336f265da239d493ff4
這里繼續說說js異步處理的方法 async await( 即Generator的語法糖)
async 是“異步”的簡寫,async 用于申明一個 function 是異步的,而 await 用于等待一個異步方法執行完成,await 只能出現在 async 函數中
同樣用上一篇中讀取文件的例子,這里改寫為
let bluebird = require('bluebird'); let fs = require('fs'); let read = bluebird.promisify(fs.readFile); //await 命令后面的 Promise 對象,運行結果可能是 //rejected,所以最好把 await 命令放在 try...catch 代碼塊中。async function r(){try{let content1 = await read('1.txt','utf8');let content2 = await read(content1,'utf8');return content2;}catch(e){ console.log('err',e)} }r().then(function(data){console.log('data',data); },function(err){console.log('err1',err); })async await和generator的寫法很像,就是將 Generator 函數的星號(*)替換成 async,將 yield 替換成await
但async 函數對 Generator 函數做了改進:
1、內置執行器:Generator函數的執行必須靠執行器,所以才有了 co 函數庫,而 async 函數自帶執行器.也就是說,async 函數的執行,與普通函數一模一樣。
2、更好的語義:async 和 await,比起星號和 yield,語義更清楚了。async 表示函數里有異步操作,await 表示緊跟在后面的表達式需要等待結果。
3、更廣的適用性: co 函數庫約定,yield 命令后面只能是 Thunk 函數或 Promise 對象,而 async 函數的 await 命令后面,可以跟 Promise 對象和原始類型的值(數值、字符串和布爾值,但這時等同于同步操作)
async 函數是非常新的語法功能,新到都不屬于 ES6,而是屬于 ES7。目前,它仍處于提案階段,但是轉碼器 Babel 和 regenerator 都已經支持,轉碼后就能使用。
async 的作用
async 函數負責返回一個 Promise 對象
如果在async函數中 return 一個直接量,async 會把這個直接量通過Promise.resolve() 封裝成 Promise 對象;
如果 async 函數沒有返回值,它會返回 Promise.resolve(undefined)
await 在等待什么
一般我們都用await去等帶一個async函數完成,不過按語法說明,await 等待的是一個表達式,這個表達式的計算結果是 Promise 對象或者其它值,所以,await后面實際可以接收普通函數調用或者直接量
如果await等到的不是一個promise對象,那跟著的表達式的運算結果就是它等到的東西;
如果是一個promise對象,await會阻塞后面的代碼,等promise對象resolve,得到resolve的值作為await表達式的運算結果
雖然await阻塞了,但await在async中,async不會阻塞,它內部所有的阻塞都被封裝在一個promise對象中異步執行
Async Await使用場景
如上面的例子,當需要用到promise鏈式調用的時候,就體現出Async Await的優勢;
假設一個業務需要分步完成,每個步驟都是異步的,而且依賴上一步的執行結果,甚至依賴之前每一步的結果,就可以使用Async Await來完成
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(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); }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();如果用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();可見用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);注意
await 命令后面的 Promise 對象,運行結果可能是 rejected,所以最好把 await 命令放在 try...catch 代碼塊中,或者await后的Promise添加catch回調
await read('1.txt','utf8').catch(function(err){console.log(err); })await 只能出現在 async 函數中,如果用在普通函數,就會報錯
async function dbFuc(db) {let docs = [{}, {}, {}];// 報錯docs.forEach(function (doc) {await db.post(doc);}); }上面代碼會報錯,因為 await 用在普通函數之中了。但是,如果將 forEach 方法的參數改成 async 函數,也有問題
async function dbFuc(db) {let docs = [{}, {}, {}];// 可能得到錯誤結果docs.forEach(async function (doc) {await db.post(doc);}); }上面代碼可能不會正常工作,原因是這時三個 db.post 操作將是并發執行,也就是同時執行,而不是繼發執行。正確的寫法是采用 for 循環。
async function dbFuc(db) {let docs = [{}, {}, {}];for (let doc of docs) {await db.post(doc);} }如果確實希望多個請求并發執行,可以使用 Promise.all 方法。
async function dbFuc(db) {let docs = [{}, {}, {}];let promises = docs.map((doc) => db.post(doc));let results = await Promise.all(promises);console.log(results); }// 或者使用下面的寫法async function dbFuc(db) {let docs = [{}, {}, {}];let promises = docs.map((doc) => db.post(doc));let results = [];for (let promise of promises) {results.push(await promise);}console.log(results); }總結
使用 async / await, 搭配 promise, 可以通過編寫形似同步的代碼來處理異步流程, 提高代碼的簡潔性和可讀性。
Async Await 的優點: 1、解決了回調地獄的問題
2、支持并發執行
3、可以添加返回值 return xxx;
4、可以在代碼中添加try/catch捕獲錯誤
參考資料
1、https://segmentfault.com/a/1190000007535316
2、http://www.ruanyifeng.com/blog/2015/05/async.html
更多專業前端知識,請上 【猿2048】www.mk2048.com
總結
以上是生活随笔為你收集整理的Async Await的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: canvas入门实战--邀请卡生成与下载
- 下一篇: 送福利:ROKID 语音开发板免费送,开