這篇文章主要介紹了Javascript中async/await是怎樣工作的,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
在坡頭等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站制作 網(wǎng)站設(shè)計(jì)制作專業(yè)公司,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),成都全網(wǎng)營(yíng)銷推廣,成都外貿(mào)網(wǎng)站制作,坡頭網(wǎng)站建設(shè)費(fèi)用合理。
async / await是ES7的重要特性之一,也是目前社區(qū)里公認(rèn)的優(yōu)秀異步解決方案。目前,async / await這個(gè)特性已經(jīng)是stage 3的建議,可以看看TC39的進(jìn)度,本篇文章將分享async / await是如何工作的,閱讀本文前,希望你具備Promise、generator、yield等ES6的相關(guān)知識(shí)。
在詳細(xì)介紹async / await之前,先回顧下目前在ES6中比較好的異步處理辦法。下面的例子中數(shù)據(jù)請(qǐng)求用Node.js中的request模塊,數(shù)據(jù)接口采用Github v3 api文檔提供的repo代碼倉(cāng)庫(kù)詳情API作為例子演示。
雖然Node.js的異步IO帶來(lái)了對(duì)高并發(fā)的良好支持,同時(shí)也讓“回調(diào)”成為災(zāi)難,很容易造成回調(diào)地獄。傳統(tǒng)的方式比如使用具名函數(shù),雖然可以減少嵌套的層數(shù),讓代碼看起來(lái)比較清晰。但是會(huì)造成比較差的編碼和調(diào)試體驗(yàn),你需要經(jīng)常使用用ctrl + f去尋找某個(gè)具名函數(shù)的定義,這使得IDE窗口經(jīng)常上下來(lái)回跳動(dòng)。使用Promise之后,可以很好的減少嵌套的層數(shù)。另外Promise的實(shí)現(xiàn)采用了狀態(tài)機(jī),在函數(shù)里面可以很好的通過(guò)resolve和reject進(jìn)行流程控制,你可以按照順序鏈?zhǔn)降娜?zhí)行一系列代碼邏輯了。下面是使用Promise的一個(gè)例子:
const request = require('request'); // 請(qǐng)求的url和header const options = { url: 'https://api.github.com/repos/cpselvis/zhihu-crawler', headers: { 'User-Agent': 'request' } }; // 獲取倉(cāng)庫(kù)信息 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);); // 此處如果是多個(gè)Promise順序執(zhí)行的話,如下: // 每個(gè)then里面去執(zhí)行下一個(gè)promise // getRepoData() // .then((value2) => {return promise2}) // .then((value3) => {return promise3}) // .then((x) => console.log(x))
不過(guò)Promise仍然存在缺陷,它只是減少了嵌套,并不能完全消除嵌套。舉個(gè)例子,對(duì)于多個(gè)promise串行執(zhí)行的情況,第一個(gè)promise的邏輯執(zhí)行完之后,我們需要在它的then函數(shù)里面去執(zhí)行第二個(gè)promise,這個(gè)時(shí)候會(huì)產(chǎn)生一層嵌套。另外,采用Promise的代碼看起來(lái)依然是異步的,如果寫的代碼如果能夠變成同步該多好??!
談到generator,你應(yīng)該不會(huì)對(duì)它感到陌生。在Node.js中對(duì)于回調(diào)的處理,我們經(jīng)常用的TJ / Co就是使用generator結(jié)合promise來(lái)實(shí)現(xiàn)的,co是coroutine的簡(jiǎn)稱,借鑒于python、lua等語(yǔ)言中的協(xié)程。它可以將異步的代碼邏輯寫成同步的方式,這使得代碼的閱讀和組織變得更加清晰,也便于調(diào)試。
const co = require('co'); const request = require('request'); const options = { url: 'https://api.github.com/repos/cpselvis/zhihu-crawler', headers: { 'User-Agent': 'request' } }; // yield后面是一個(gè)生成器 generator const getRepoData = function* () { return new Promise((resolve, reject) => { request(options, (err, res, body) => { if (err) { reject(err); } resolve(body); }); }); }; co(function* () { const result = yield getRepoData; // ... 如果有多個(gè)異步流程,可以放在這里,比如 // const r1 = yield getR1; // const r2 = yield getR2; // const r3 = yield getR3; // 每個(gè)yield相當(dāng)于暫停,執(zhí)行yield之后會(huì)等待它后面的generator返回值之后再執(zhí)行后面其它的yield邏輯。 return result; }).then(function (value) { console.log(value); }, function (err) { console.error(err.stack); });
雖然co是社區(qū)里面的優(yōu)秀異步解決方案,但是并不是語(yǔ)言標(biāo)準(zhǔn),只是一個(gè)過(guò)渡方案。ES7語(yǔ)言層面提供async / await去解決語(yǔ)言層面的難題。目前async / await 在 IE edge中已經(jīng)可以直接使用了,但是chrome和Node.js還沒(méi)有支持。幸運(yùn)的是,babel已經(jīng)支持async的transform了,所以我們使用的時(shí)候引入babel就行。在開(kāi)始之前我們需要引入以下的package,preset-stage-3里就有我們需要的async/await的編譯文件。
無(wú)論是在Browser還是Node.js端都需要安裝下面的包。
$ npm install babel-core --save $ npm install babel-preset-es2015 --save $ npm install babel-preset-stage-3 --save
這里推薦使用babel官方提供的require hook方法。就是通過(guò)require進(jìn)來(lái)后,接下來(lái)的文件進(jìn)行require的時(shí)候都會(huì)經(jīng)過(guò)Babel的處理。因?yàn)槲覀冎繡ommonJs是同步的模塊依賴,所以也是可行的方法。這個(gè)時(shí)候,需要編寫兩個(gè)文件,一個(gè)是啟動(dòng)的js文件,另外一個(gè)是真正執(zhí)行程序的js文件。
啟動(dòng)文件index.js
require('babel-core/register'); require('./async.js');
真正執(zhí)行程序的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類似,如果有多個(gè)異步流程,可以放在這里,比如 // const r1 = await getR1(); // const r2 = await getR2(); // const r3 = await getR3(); // 每個(gè)await相當(dāng)于暫停,執(zhí)行await之后會(huì)等待它后面的 //函數(shù)(不是generator)返回值之后再執(zhí)行后面其它的await邏輯。 return value; } catch (err) { console.log(err); } } asyncFun().then(x => console.log(`x: ${x}`)).catch(err => console.error(err));
注意點(diǎn):
async用來(lái)申明里面包裹的內(nèi)容可以進(jìn)行同步的方式執(zhí)行,await則是進(jìn)行執(zhí)行順序控制,每次執(zhí)行一個(gè)await,程序都會(huì)暫停等待await返回值,然后再執(zhí)行之后的await。
await后面調(diào)用的函數(shù)需要返回一個(gè)promise,另外這個(gè)函數(shù)是一個(gè)普通的函數(shù)即可,而不是generator。
await只能用在async函數(shù)之中,用在普通函數(shù)中會(huì)報(bào)錯(cuò)。
await命令后面的 Promise 對(duì)象,運(yùn)行結(jié)果可能是 rejected,所以最好把 await 命令放在 try...catch 代碼塊中。
其實(shí),async / await的用法和co差不多,await和yield都是表示暫停,外面包裹一層async 或者 co來(lái)表示里面的代碼可以采用同步的方式進(jìn)行處理。不過(guò)async / await里面的await后面跟著的函數(shù)不需要額外處理,co是需要將它寫成一個(gè)generator的。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“Javascript中async/await是怎樣工作的”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!