語法上,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
因爲 Generator 函數返回的遍歷器對象,只有調用next方法纔會遍歷下一個內部狀態,因此其實提供了一種能夠暫停執行的函數。yield表達式就是暫停標誌。函數
遍歷器對象的next方法的運行邏輯以下。ui
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 函數返回的遍歷器對象,還有一個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代碼塊執行完再執行。
Promise 異步操做有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)。除了異步操做的結果,任何其餘操做都沒法改變這個狀態。
Promise 對象只有:從 pending 變爲 fulfilled 和從 pending 變爲 rejected 的狀態改變。只要處於 fulfilled 和 rejected ,狀態就不會再變了即 resolved(已定型)。
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方法是.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
});
複製代碼
finally方法用於指定無論 Promise 對象最後狀態如何,都會執行的操做。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···})
複製代碼
上面代碼中,無論promise最後的狀態,在執行完then或catch指定的回調函數之後,都會執行finally方法指定的回調函數。
Promise.all方法用於將多個 Promise 實例,包裝成一個新的 Promise 實例。
const p = Promise.all([p1, p2, p3])
複製代碼
上面代碼中,Promise.all方法接受一個數組做爲參數,p一、p二、p3都是 Promise,p的狀態由p一、p二、p3決定,分紅兩種狀況。
Promise.race方法一樣是將多個 Promise 實例,包裝成一個新的 Promise 實例。
const p = Promise.race([p1, p2, p3])
複製代碼
上面代碼中,只要p一、p二、p3之中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的Promise實例的返回值,就傳遞給p的回調函數。
有時須要將現有對象轉爲 Promise 對象,Promise.resolve方法就起到這個做用。
參數是一個 Promise 實例
若是參數是 Promise 實例,那麼Promise.resolve將不作任何修改、原封不動地返回這個實例。
參數是一個thenable對象
Promise.resolve方法會將這個對象轉爲 Promise 對象,而後就當即執行thenable對象的then方法。
參數不是具備then方法的對象,或根本就不是對象
若是參數是一個原始值,或者是一個不具備then方法的對象,則Promise.resolve方法返回一個新的 Promise 對象,狀態爲resolved。
不帶有任何參數
Promise.resolve()方法容許調用時不帶參數,直接返回一個resolved狀態的 Promise 對象。
Promise.reject(reason)方法也會返回一個新的 Promise 實例,該實例的狀態爲rejected。
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)
})
複製代碼
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: 出錯了
複製代碼
async function f() {
// 等同於
// return 123;
return await 123;
}
f().then(v => console.log(v))
// 123
複製代碼
// good
async function myFunction() {
try {
await somethingThatReturnsAPromise();
} catch (err) {
console.log(err);
}
}
// bad
async function myFunction() {
await somethingThatReturnsAPromise()
.catch(function (err) {
console.log(err)
})
}
複製代碼
// good
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// bad
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
複製代碼