ES(2017)學習筆記(十一)【Async】

風靈使發表於2019-04-06

async 函式

async函式屬於ES7,是一個Generator函式的語法糖。
async函式可以自動執行Generator函式。
async函式的實現原理,就是將Generator函式和自動執行器,包裝在一個函式裡。

async function fn(args) {
    // ...
}

// 等同於

function fn(args) {
    //spawn函式是自動執行器
    return spawn(function* () {
    // ...
    });
}

//spawn函式的實現
function spawn(genF) {
    return new Promise((resolve, reject) => {
        let gen = genF();
        function step(nextF) {
            try {
                var next = nextF();
            } catch(e) {
                return reject(e);
            }
            if(next.done) {
                return resolve(next.value);
            }
            //使用Promise.resolve使其可以支援原始型別的值,保證返回一個Promise物件
            Promise.resolve(next.value)
            .then(v => step( () => gen.next(v) )  //呼叫後next.value作為結果返回
            , e => step( () => gen.throw(e) ));
        }
        //呼叫step進行遞迴執行gen
        step( () => gen.next(undefined) );
    });
}

co模組使用的區別在於:

  • co模組執行的Generator函式的yield語句後可以是Promise物件或者Thunk函式。
  • async函式await後可以是Promise物件或者原始型別的值(數值、字串和布林值,但這時等同於同步操作)。
const fs = require('fs');
const co = require('co');

//將非同步封裝到Promise
const readFile  = function (fileName) {
    return new Promise(function(resolve, reject) {
        fs.readFile(fileName, function(error, data) {
            if(error) return reject(error);
            resolve(data);
        })
    });
};

//用生成器函式和co實現
const gen = function* (){
    const f1 = yield readFile('/ect/f1');
    const f2 = yield readFile('/ect/f2');
}
co(gen);        //返回一個Promise物件

//用async函式實現
const asyncReadFile = async function() {
    const f1 = await readFile('/ect/f1');
    const f2 = await readFile('/ect/f2');
}
let result = asyncReadFile();   //返回一個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);

async的錯誤處理機制

async函式的語法規則總體上比較簡單,難點是錯誤處理機制。

返回Promise物件

async函式返回一個Promise物件。
async函式內部return語句返回的值,會成為then方法回撥函式的引數。
async函式內部丟擲錯誤,會導致返回的Promise物件變為reject狀態。

async function f() {
  throw new Error('出錯了');
}

f().then(
  v => console.log(v),
  e => console.log(e)   //被呼叫
)
// Error: 出錯了

返回的Promise的狀態,必須等到內部所有await命令後面的Promise物件執行完,才會發生狀態改變,除非遇到return語句或者丟擲錯誤。
await命令後面的Promise物件如果變為reject狀態,則reject的引數會被catch方法的回撥函式接收到。

async function f() {
  await Promise.reject('出錯了');
  await Promise.resolve('hello world'); // 不會執行
}

f()
.then(v => console.log(v))
.catch(e => console.log(e))
// 出錯了

相關文章