Javascript中的async await
async / await是ES7的重要特性之一,也是目前社區里公認的優秀異步解決方案。目前,async / await這個特性已經是stage 3的建議,可以看看TC39的進度,本篇文章將分享async / await是如何工作的,閱讀本文前,希望你具備Promise、generator、yield等ES6的相關知識。
在詳細介紹async / await之前,先回顧下目前在ES6中比較好的異步處理辦法。下面的例子中數據請求用Node.js中的request模塊,數據接口采用Github v3 api文檔提供的repo代碼倉庫詳情API作為例子演示。
Promise對異步的處理
雖然Node.js的異步IO帶來了對高并發的良好支持,同時也讓“回調”成為災難,很容易造成回調地獄。傳統的方式比如使用具名函數,雖然可以減少嵌套的層數,讓代碼看起來比較清晰。但是會造成比較差的編碼和調試體驗,你需要經常使用用ctrl + f去尋找某個具名函數的定義,這使得IDE窗口經常上下來回跳動。使用Promise之后,可以很好的減少嵌套的層數。另外Promise的實現采用了狀態機,在函數里面可以很好的通過resolve和reject進行流程控制,你可以按照順序鏈式的去執行一系列代碼邏輯了。下面是使用Promise的一個例子:
const?request?=?require('request');//?請求的url和headerconst?options?=?{url:?'https://api.github.com/repos/cpselvis/zhihu-crawler',headers:?{'User-Agent':?'request'}};//?獲取倉庫信息const?getRepoData?=?()?=>?{return?new?Promise((resolve,?reject)?=>?{request(options,?(err,?res,?body)?=>?{if?(err)?{reject(err);}resolve(body);});});};getRepoData().then((result)?=>?console.log(result);).catch((reason)?=>?console.error(reason););//?此處如果是多個Promise順序執行的話,如下://?每個then里面去執行下一個promise//?getRepoData()//???.then((value2)?=>?{return?promise2})//???.then((value3)?=>?{return?promise3})//???.then((x)?=>?console.log(x))不過Promise仍然存在缺陷,它只是減少了嵌套,并不能完全消除嵌套。舉個例子,對于多個promise串行執行的情況,第一個promise的邏輯執行完之后,我們需要在它的then函數里面去執行第二個promise,這個時候會產生一層嵌套。另外,采用Promise的代碼看起來依然是異步的,如果寫的代碼如果能夠變成同步該多好啊!
Generator對異步的處理
談到generator,你應該不會對它感到陌生。在Node.js中對于回調的處理,我們經常用的TJ / Co就是使用generator結合promise來實現的,co是coroutine的簡稱,借鑒于python、lua等語言中的協程。它可以將異步的代碼邏輯寫成同步的方式,這使得代碼的閱讀和組織變得更加清晰,也便于調試。
const?co?=?require('co');const?request?=?require('request');const?options?=?{url:?'https://api.github.com/repos/cpselvis/zhihu-crawler',headers:?{'User-Agent':?'request'}};//?yield后面是一個生成器?generatorconst?getRepoData?=?function*?()?{return?new?Promise((resolve,?reject)?=>?{request(options,?(err,?res,?body)?=>?{if?(err)?{reject(err);}resolve(body);});});};co(function*?()?{const?result?=?yield?getRepoData;//?...?如果有多個異步流程,可以放在這里,比如//?const?r1?=?yield?getR1;//?const?r2?=?yield?getR2;//?const?r3?=?yield?getR3;//?每個yield相當于暫停,執行yield之后會等待它后面的generator返回值之后再執行后面其它的yield邏輯。return?result;}).then(function?(value)?{console.log(value);},?function?(err)?{console.error(err.stack);});async / await對異步的處理
雖然co是社區里面的優秀異步解決方案,但是并不是語言標準,只是一個過渡方案。ES7語言層面提供async / await去解決語言層面的難題。目前async / await 在 IE edge中已經可以直接使用了,但是chrome和Node.js還沒有支持。幸運的是,babel已經支持async的transform了,所以我們使用的時候引入babel就行。在開始之前我們需要引入以下的package,preset-stage-3里就有我們需要的async/await的編譯文件。
無論是在Browser還是Node.js端都需要安裝下面的包。
$?npm?install?babel-core?--save$?npm?install?babel-preset-es2015?--save$?npm?install?babel-preset-stage-3?--save這里推薦使用babel官方提供的require hook方法。就是通過require進來后,接下來的文件進行require的時候都會經過Babel的處理。因為我們知道CommonJs是同步的模塊依賴,所以也是可行的方法。這個時候,需要編寫兩個文件,一個是啟動的js文件,另外一個是真正執行程序的js文件。
啟動文件index.js
require('babel-core/register');require('./async.js');真正執行程序的async.js
const?request?=?require('request');const?options?=?{url:?'https://api.github.com/repos/cpselvis/zhihu-crawler',headers:?{'User-Agent':?'request'}};const?getRepoData?=?()?=>?{return?new?Promise((resolve,?reject)?=>?{request(options,?(err,?res,?body)?=>?{if?(err)?{reject(err);}resolve(body);});});};async?function?asyncFun()?{try?{const?value?=?await?getRepoData();//?...?和上面的yield類似,如果有多個異步流程,可以放在這里,比如//?const?r1?=?await?getR1();//?const?r2?=?await?getR2();//?const?r3?=?await?getR3();//?每個await相當于暫停,執行await之后會等待它后面的函數(不是generator)返回值之后再執行后面其它的await邏輯。return?value;}?catch?(err)?{console.log(err);}}asyncFun().then(x?=>?console.log(`x:?${x}`)).catch(err?=>?console.error(err));注意點:
async用來申明里面包裹的內容可以進行同步的方式執行,await則是進行執行順序控制,每次執行一個await,程序都會暫停等待await返回值,然后再執行之后的await。
await后面調用的函數需要返回一個promise,另外這個函數是一個普通的函數即可,而不是generator。
await只能用在async函數之中,用在普通函數中會報錯。
await命令后面的 Promise 對象,運行結果可能是 rejected,所以最好把 await 命令放在 try...catch 代碼塊中。
其實,async / await的用法和co差不多,await和yield都是表示暫停,外面包裹一層async 或者 co來表示里面的代碼可以采用同步的方式進行處理。不過async / await里面的await后面跟著的函數不需要額外處理,co是需要將它寫成一個generator的。
轉載于:https://blog.51cto.com/12902932/1925694
總結
以上是生活随笔為你收集整理的Javascript中的async await的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: leetcode第一刷_Search i
- 下一篇: Java ThreadLocal的使用