ES6新特徵總結與介紹——異步編程

1、Generator

(一)基本概念

語法上,Generator 函數是一個狀態機,封裝了多個內部狀態。執行Generator函數會返回一個遍歷器對象,也就是說,Generator函數除了狀態機,仍是一個遍歷器對象生成函數。返回的遍歷器對象,能夠依次遍歷 Generator 函數內部的每個狀態。數組

形式上,Generator 函數是一個普通函數,可是有兩個特徵。一是,function關鍵字與函數名之間有一個星號;二是,函數體內部使用yield表達式,定義不一樣的內部狀態。promise

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();
複製代碼

上面代碼定義了一個 Generator 函數helloWorldGenerator,它內部有兩個yield表達式(hello和world),即該函數有三個狀態:hello,world 和 return 語句(結束執行)。bash

Generator 函數的調用方法與普通函數同樣,也是在函數名後面加上一對圓括號。不一樣的是,調用Generator函數後,該函數並不執行,返回的也不是函數運行結果,而是一個指向內部狀態的指針對象,也就是遍歷器對象(Iterator Object)。異步

下一步,必須調用遍歷器對象的next方法,使得指針移向下一個狀態。也就是說,每次調用next方法,內部指針就從函數頭部或上一次停下來的地方開始執行,直到遇到下一個yield表達式(或return語句)爲止。換言之,Generator函數是分段執行的,yield表達式是暫停執行的標記,而next方法能夠恢復執行。async

(二)yield

因爲 Generator 函數返回的遍歷器對象,只有調用next方法纔會遍歷下一個內部狀態,因此其實提供了一種能夠暫停執行的函數。yield表達式就是暫停標誌。函數

(三)Generator.prototype.next()

遍歷器對象的next方法的運行邏輯以下。ui

  1. 遇到yield表達式,就暫停執行後面的操做,並將緊跟在yield後面的那個表達式的值,做爲返回的對象的value屬性值。
  2. 下一次調用next方法時,再繼續往下執行,直到遇到下一個yield表達式。
  3. 若是沒有再遇到新的yield表達式,就一直運行到函數結束,直到return語句爲止,並將return語句後面的表達式的值,做爲返回的對象的value屬性值。
  4. 若是該函數沒有return語句,則返回的對象的value屬性值爲undefined。

(四)Generator.prototype.throw()

Generator 函數返回的遍歷器對象,都有一個throw方法,能夠在函數體外拋出錯誤,而後在 Generator 函數體內捕獲。spa

var g = function* () {
  try {
    yield;
  } catch (e) {
    console.log('內部捕獲', e);
  }
};

var i = g();
i.next();

try {
  i.throw('a');
  i.throw('b');
} catch (e) {
  console.log('外部捕獲', e);
}
// 內部捕獲 a
// 外部捕獲 b
複製代碼

上面代碼中,遍歷器對象i連續拋出兩個錯誤。第一個錯誤被Generator函數體內的catch語句捕獲。i第二次拋出錯誤,因爲Generator函數內部的catch語句已經執行過了,不會再捕捉到這個錯誤了,因此這個錯誤就被拋出了 Generator 函數體,被函數體外的catch語句捕獲。prototype

(五)Generator.prototype.return()

Generator 函數返回的遍歷器對象,還有一個return方法,能夠返回給定的值,而且終結遍歷 Generator 函數。指針

function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

var g = gen();

g.next()        // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next()        // { value: undefined, done: true }
複製代碼

若是return方法調用時,不提供參數,則返回值的value屬性爲undefined。

若是 Generator 函數內部有try...finally代碼塊,且正在執行try代碼塊,那麼return方法會推遲到finally代碼塊執行完再執行。

2、Promise

(一)Promise 狀態

Promise 異步操做有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)。除了異步操做的結果,任何其餘操做都沒法改變這個狀態。

Promise 對象只有:從 pending 變爲 fulfilled 和從 pending 變爲 rejected 的狀態改變。只要處於 fulfilled 和 rejected ,狀態就不會再變了即 resolved(已定型)。

(二)Promise.prototype.then()

then 方法接收兩個函數做爲參數,then方法的第一個參數是resolved狀態的回調函數,第二個參數(可選)是rejected狀態的回調函數。兩個函數只會有一個被調用。

const promise = new Promise(function(resolve, reject) {
  if (/* 異步操做成功 */){
    resolve(value)
  } else {
    reject(error)
  }
})
promise.then(function(value) {
  // success
}, function(error) {
  // failure
})
複製代碼

(三)Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的別名,用於指定發生錯誤時的回調函數。 通常來講,不要在then方法裏面定義 Reject 狀態的回調函數(即then的第二個參數),老是使用catch方法。

// bad
promise
  .then(function(data) {
    // success
  }, function(err) {
    // error
  });

// good
promise
  .then(function(data) { //cb
    // success
  })
  .catch(function(err) {
    // error
  });
複製代碼

(四)Promise.prototype.finally()

finally方法用於指定無論 Promise 對象最後狀態如何,都會執行的操做。

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···})
複製代碼

上面代碼中,無論promise最後的狀態,在執行完then或catch指定的回調函數之後,都會執行finally方法指定的回調函數。

(五)Promise.all()

Promise.all方法用於將多個 Promise 實例,包裝成一個新的 Promise 實例。

const p = Promise.all([p1, p2, p3])
複製代碼

上面代碼中,Promise.all方法接受一個數組做爲參數,p一、p二、p3都是 Promise,p的狀態由p一、p二、p3決定,分紅兩種狀況。

  1. 只有p一、p二、p3的狀態都變成fulfilled,p的狀態纔會變成fulfilled,此時p一、p二、p3的返回值組成一個數組,傳遞給p的回調函數。
  2. 只要p一、p二、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。

(六)Promise.race()

Promise.race方法一樣是將多個 Promise 實例,包裝成一個新的 Promise 實例。

const p = Promise.race([p1, p2, p3])
複製代碼

上面代碼中,只要p一、p二、p3之中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的Promise實例的返回值,就傳遞給p的回調函數。

(七)Promise.resolve()

有時須要將現有對象轉爲 Promise 對象,Promise.resolve方法就起到這個做用。

  1. 參數是一個 Promise 實例

    若是參數是 Promise 實例,那麼Promise.resolve將不作任何修改、原封不動地返回這個實例。

  2. 參數是一個thenable對象

    Promise.resolve方法會將這個對象轉爲 Promise 對象,而後就當即執行thenable對象的then方法。

  3. 參數不是具備then方法的對象,或根本就不是對象

    若是參數是一個原始值,或者是一個不具備then方法的對象,則Promise.resolve方法返回一個新的 Promise 對象,狀態爲resolved。

  4. 不帶有任何參數

    Promise.resolve()方法容許調用時不帶參數,直接返回一個resolved狀態的 Promise 對象。

(八)Promise.reject()

Promise.reject(reason)方法也會返回一個新的 Promise 實例,該實例的狀態爲rejected。

3、async

(一)基本用法

async函數返回一個 Promise 對象,可使用then方法添加回調函數。當函數執行的時候,一旦遇到await就會先返回,等到異步操做完成,再接着執行函數體內後面的語句。

async function getStockPriceByName(name) {
  const symbol = await getStockSymbol(name)
  const stockPrice = await getStockPrice(symbol)
  return stockPrice
}

getStockPriceByName('goog').then(function (result) {
  console.log(result)
})
複製代碼

(二)返回 Promise 對象

async函數返回一個 Promise 對象。 async函數內部return語句返回的值,會成爲then方法回調函數的參數。

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

f().then(v => console.log(v))
// "hello world"
複製代碼

上面代碼中,函數f內部return命令返回的值,會被then方法回調函數接收到。

async函數內部拋出錯誤,會致使返回的 Promise 對象變爲reject狀態。拋出的錯誤對象會被catch方法回調函數接收到。

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

f().then(
  v => console.log(v),
  e => console.log(e)
)
// Error: 出錯了
複製代碼

(三)await 命令

  1. 正常狀況下,await命令後面是一個 Promise 對象,返回該對象的結果。若是不是 Promise 對象,就直接返回對應的值。
async function f() {
  // 等同於
  // return 123;
  return await 123;
}

f().then(v => console.log(v))
// 123
複製代碼
  1. 另外一種狀況是,await命令後面是一個thenable對象(即定義then方法的對象),那麼await會將其等同於 Promise 對象。

(四)注意點

  1. await命令後面的Promise對象,運行結果多是rejected,因此最好把await命令放在try...catch代碼塊中。
// good
async function myFunction() {
  try {
    await somethingThatReturnsAPromise();
  } catch (err) {
    console.log(err);
  }
}

// bad
async function myFunction() {
  await somethingThatReturnsAPromise()
  .catch(function (err) {
    console.log(err)
  })
}
複製代碼
  1. 多個await命令後面的異步操做,若是不存在繼發關係,最好讓它們同時觸發。
// good
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// bad
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
複製代碼
  1. await命令只能用在async函數之中,若是用在普通函數,就會報錯。
  2. async 函數能夠保留運行堆棧.
相關文章
相關標籤/搜索