首頁 >web前端 >js教程 >js中async函數使用方法詳解

js中async函數使用方法詳解

php中世界最好的语言
php中世界最好的语言原創
2018-05-22 09:44:584134瀏覽

這次帶給大家js中async函數使用方法詳解,js中async函數使用的注意事項有哪些,下面就是實戰案例,一起來看一下。

一、終極解決

非同步操作是JavaScript 程式設計的麻煩事,麻煩到一直有人提出各種各樣的方案,試圖解決這個問題。

從最早的回呼函數,到 Promise 對象,再到 Generator 函數,每次都有所改進,但又讓人覺得不徹底。它們都有額外的複雜性,都需要理解抽象的底層運作機制。

非同步I/O不就是讀取一個檔案嗎,幹嘛要搞得這麼複雜?非同步程式設計的最高境界,就是根本不用在意它是不是非同步。

async 函數就是隧道盡頭的亮光,很多人認為它是非同步操作的終極解決方案。

二、async 函數是什麼?

一句話,async 函數就是 Generator 函數的語法糖。

前文有一個 Generator 函數,依序讀取兩個檔案。

var fs = require('fs');
var readFile = function (fileName){
 return new Promise(function (resolve, reject){
  fs.readFile(fileName, function(error, data){
   if (error) reject(error);
   resolve(data);
  });
 });
};
var gen = function* (){
 var f1 = yield readFile('/etc/fstab');
 var f2 = yield readFile('/etc/shells');
 console.log(f1.toString());
 console.log(f2.toString());
};

寫成 async 函數,就是下面這樣。

var asyncReadFile = async function (){
 var f1 = await readFile('/etc/fstab');
 var f2 = await readFile('/etc/shells');
 console.log(f1.toString());
 console.log(f2.toString());
};

一比較就會發現,async 函數就是將 Generator 函數的星號(*)替換成 async,將 yield 替換成 await,僅此而已。

三、async 函數的優點

async 函數對 Generator 函數的改進,體現在以下三點。

(1)內建執行器。 Generator 函數的執行必須靠執行器,所以才有了 co 函式庫,而 async 函式自帶執行器。也就是說,async 函數的執行,與一般函數一模一樣,只要一行。

var result = asyncReadFile();

(2)更好的語意。 async 和 await,比起星號和 yield,語意更清楚了。 async 表示函數裡有非同步操作,await 表示緊接在後面的表達式需要等待結果。

(3)更廣的適用性。 co 函數庫約定,yield 指令後面只能是Thunk 函數或Promise 對象,而async 函數的await 指令後面,可以跟Promise 物件和原始型別的值(數值、字串和布林值,但這時等同於同步操作)。

四、async 函數的實作

async 函數的實現,就是將 Generator 函數和自動執行器,包裝在一個函數裡。

async function fn(args){
 // ...
}
// 等同于
function fn(args){ 
 return spawn(function*() {
  // ...
 }); 
}

所有的 async 函數都可以寫成上面的第二種形式,其中的 spawn 函數就是自動執行器。

以下給出 spawn 函數的實現,基本上就是前文自動執行器的翻版。

function spawn(genF) {
 return new Promise(function(resolve, reject) {
  var gen = genF();
  function step(nextF) {
   try {
    var next = nextF();
   } catch(e) {
    return reject(e); 
   }
   if(next.done) {
    return resolve(next.value);
   } 
   Promise.resolve(next.value).then(function(v) {
    step(function() { return gen.next(v); });   
   }, function(e) {
    step(function() { return gen.throw(e); });
   });
  }
  step(function() { return gen.next(undefined); });
 });
}

async 函數是非常新的語法功能,新到都不屬於 ES6,而是屬於 ES7。目前,它仍處於提案階段,但是轉碼器 Babel 和 regenerator 都已經支持,轉碼後就能使用。

五、async 函數的用法

#同 Generator 函數一樣,async 函數傳回一個 Promise 對象,可以使用 then 方法加入回調函數。當函數執行的時候,一旦遇到 await 就會先回,等到觸發的非同步操作完成,再接著執行函數體內後面的語句。

下面是一個例子。

async function getStockPriceByName(name) {
 var symbol = await getStockSymbol(name);
 var stockPrice = await getStockPrice(symbol);
 return stockPrice;
}
getStockPriceByName('goog').then(function (result){
 console.log(result);
});

上面程式碼是一個取得股票報價的函數,函數前面的async關鍵字,表示該函數內部有非同步操作。呼叫函數時,會立即傳回一個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);

上面程式碼指定50毫秒以後,輸出"hello world"。

六、注意點

await 指令後面的Promise 對象,運行結果可能是rejected,所以最好把await 指令放在try ...catch 程式碼區塊中。

async function myFunction() {
 try {
  await somethingThatReturnsAPromise();
 } catch (err) {
  console.log(err);
 }
}
// 另一种写法
async function myFunction() {
 await somethingThatReturnsAPromise().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);
}

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

Nodejs内存治理步骤详解

Vue页面骨架屏注入步骤详解

以上是js中async函數使用方法詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn