天天看點

ES6 async function

ES2017

 标準引入了 

async

 函數,使得異步操作變得更加友善,由于

async函數

傳回的是

Promise

對象,可以作為

await

指令的參數。

async function timeout(ms) {
  await new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}

asyncPrint('hello world', 50);      

傳回 

Promise

 對象

async function f() {
  return 'hello world';
}

f().then(v => console.log(v))
// "hello world"      

async

函數内部抛出錯誤,會導緻傳回的 

Promise

 對象變為

reject

狀态。抛出的錯誤對象會被

catch

方法回調函數接收到
async function f() {
  throw new Error('出錯了');
}

f().then(
  v => console.log('resolve', v),
  e => console.log('reject', e)
)
//reject Error: 出錯了      

Promise

 對象的狀态變化

async

函數傳回的 

Promise

 對象,必須等到内部所有

await

指令後面的 

Promise

 對象執行完,才會發生狀态改變,除非遇到

return

語句或者抛出錯誤。也就是說,隻有

async

函數内部的異步操作執行完,才會執行

then

方法指定的回調函數。

async function getTitle(url) {
  let response = await fetch(url);
  let html = await response.text();
  return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle('http://localhost:8080/').then(console.log(123))      

await

 指令

正常情況下,

await

指令後面是一個 

Promise

 對象,傳回該對象的結果。如果不是 

Promise

 對象,就直接傳回對應的值。

async function f() {
  // 等同于
  // return 123;
  return await 123;
}

f().then(v => console.log(v))
// 123      

任何一個

await

語句後面的 

Promise

reject

狀态,那麼整個

async

函數都會中斷執行。

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

如果希望即使前一個異步操作失敗,也不要中斷後面的異步操作。這時可以将第一個

await

放在

try...catch

結構裡面,這樣不管這個異步操作是否成功,第二個

await

都會執行。

async function f() {
  try {
    await Promise.reject('出錯了');
  } catch(e) {
  }
  return await Promise.resolve('hello world');
}

f()
.then(v => console.log(v))
// hello world      

async

 函數的實作原理

将 Generator 函數和自動執行器,包裝在一個函數裡
async function fn(args) {
  // ...
}

// 等同于

function fn(args) {
  return spawn(function* () {
    // ...
  });
}

function spawn(genF) {
  return new Promise(function(resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        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); });
  });
}