1. 程式人生 > >非同步程式設計的 async/await

非同步程式設計的 async/await

async/await 和 Generators + co 的寫法非常的相似,只是把用於宣告 Generator 函式的 * 關鍵字替換成了 async 並寫在了 function 關鍵字的前面,把 yield 關鍵字替換成了 await;另外,async 函式是基於 Promise 的,await 關鍵字後面等待的非同步操作必須是一個 Promise 例項,當然也可以是原始型別的值,只不過這時的執行效果等同於同步,與 Generator 不同的是,await 關鍵字前可以使用變數去接收這個正在等待的 Promise 例項執行後的結果。

async 函式的基本用法

async 函式返回一個 Promise 例項,可以使用 then 方法添加回調函式。當函式執行的時候,只要遇到 await 就會等待,直到 await 後面的同步或非同步操作完成,再接著執行函式體內後面的語句。

ES6的宣告

    // 1- 函式宣告
    function  * fn() {}

    // 2- 函式表示式
    const * fn = function() {};

ES7的宣告

// 1- 函式宣告
async function fn() {}

// 2- 函式表示式
const fn = async function() {};

// 3- 箭頭函式
const fn = async () => {};

// 4- 作為物件的方法
let obj = {
    async fn() {}
};

// 5- 作為 class 的方法
class Person(name) {
    constructor () {
        this
.name = name; } async getName() { const name = await this.name; return name; } }

使用 NodeJS 的 fs 模組連續非同步讀檔案,第一個檔名為 a.txt,讀到的內容為 b.txt,作為要讀的第二個檔案的檔名,繼續讀 b.txt 後將讀到的內容 “Hello world” 打印出來。
我們來使用 async/await 的方式來實現一下:

// 引入依賴
let fs = require("fs");
let util = require("util"
); // 將 fs.readFile 轉換成 Promise let readFile = util.promisify(fs.readFile); // 宣告 async 函式 async function read(file) { let aData = await readFile(file, "utf8"); let bData = await readFile(aData, "utf8"); return bData; } // 呼叫 async 函式 read("a.txt").then(data => { console.log(data); // Hello world });

與 Generator 函式一樣,寫法像同步,執行是非同步,不同的是我們即沒有手動呼叫 next 方法,也沒有藉助 co 庫,其實是 async 函式內部集成了類似於 co 的執行器,幫我們在非同步完成後自動向下執行程式碼,所以說 async/await 是 Generators + co 的語法糖。

await 非同步併發

在 async 函式中,如果有多個 await 互不依賴,這種情況下如果執行一個,等待一個完成,再執行一個,再等待完成,這樣是很浪費效能的,所以我們要把這些非同步操作同時觸發。

假設我們非同步讀取兩個檔案,且這兩個檔案不相關,我可以使用下面的方式來實現:

// 前置
let fs = require("fs");
let util = require("util");
let readFile = util.promisify(fs.readFile);

// 方法1- 需要改進的 async 函式
async function fn() {
    let aData = await readFile("a.txt", "utf8");
    let bData = await readFile("b.txt", "utf8");
    return [aData, bData];
}

fn();

// 方法2- 在 async 函式外部觸發非同步
let aDataPromise = readFile("a.txt", "utf8");
let bDataPromise = readFile("b.txt", "utf8");

async function fn() {
    let aData = await aDataPromise;
    let bData = await bDataPromise;
    return [aData, bData];
}

fn();

// 方法3-  使用 Promise.all
async function fn() {
    let dataArr = await Promise.all(
        readFile("a.txt", "utf8"),
        readFile("a.txt", "utf8")
    );
    return dataArr;
}

fn();

非同步併發例項

// 建立 Promise 例項
let p1 = Promise.resolve("p1 success");
let p2 = Promise.resolve("p2 success");
let p3 = Promise.resolve("p3 success");


// 1-錯誤的處理方式  async 函式
async function fn(promises) {
    promise.forEach(function (promise) {
        await promise;
    });
}

fn([p1, p2, p3]); // 執行時報錯


// 2-正確的開啟方式  修改方式
async function fn(promises) {
    for(let i = 0; i < promises.length; i++) {
        await promises[i];
    }
}

fn([p1, p2, p3]); // 正常執行