Promise與async/await與Generator
Promise是什麼:
Promise是異步微任務(process.nextTick、Promise.then() catch() finally()等),用於解決異步多層嵌套回調的問題(回調地獄–小球做正方形路徑運動),讓代碼的可讀性更高、更容易維護
Promise使用:
Promise是ES6提供的一個構造函數,可以使用Promise構造函數new出一個實例,Promise構造函數接受一個函數作為參數,這個函數有兩個參數,分別是resolve和reject;
resolve將Promise的狀態由等待變為成功(resolved),將異步操作的結果作為參數傳遞出去;
reject將Promise的狀態變為失敗(rejected),在異步操作失敗時調用,將異步操作報錯的錯誤作為參數傳遞過去。
實例創建完成後,可以使用then方法分別指定成功或者失敗的回調回調函數,也可以使用catch捕獲失敗,then和catch最終返回的也是一個Promise,所以可以鏈式調用
Promise的特點:
1.對象狀態不受外界影響(Promise對象代表一個異步操作,有三種狀態) – pending(等待狀態) – resolved(成功狀態) – rejected(失敗狀態)
2.一旦狀態改變,就不會再變化,任何時候都可以得到這個結果Promise對象狀態的改變只有兩種可能,
pending => resolved (then第一個回調)和 pending => rejected(then第二個回調)
(這兩個狀態為結束狀態,表示Promise的生命周期已結束)
3.resolve方法中的參數是then中回調函數的參數,reject方法中的參數是catch中的參數
4.then方法和catch方法返回的都是成功狀態的Promise(catch中throw返回失敗狀態的Promise)
Promise的其他方法:
Promise.finally():當promise狀態發生變化時執行(任何變化都執行),不變化不執行
Promise.resolve(value):返回成功狀態的Promise對象,並將value轉遞給對應的then方法
(當resolve函數接收的是promise對象時,後面的then會根據傳遞的promise對象的狀態變化決定執行哪一個回調)
Promise.reject():返回一個失敗狀態的Promise對象,並將給定的失敗信息傳遞給對應的處理方法
Promise.any():接受一個promise對象集合,當其中的第一個promise成功時,就返回那個成功的promise值,不會等待其他promise全部完成
Promise.all():返回一個新的promise對象,該promise對象在參數對象里接收多個promise對象,參數中的promise都成功時才會觸發成功,任意一個promise對象失敗都會觸發失敗
Promise.race():使用第一個返回的promise實例對象,成功就是成功,失敗就是失敗
Promise.allSettled():該方法的狀態無傳入promise的狀態無關,它永遠都是成功的,只會記錄下各個promise的表現
(Promise.all、Promise.race、Promise.allSettled傳入的若不是promise數組,會將其轉換成promise數組,任何可遍歷對象都可作為數組)
async/await:
async/await是基於Promise實現的,使得異步代碼看起來像同步代碼,是寫異步代碼的新方式
async/await實際上是Generator的語法糖。顧名思義,async關鍵字,代表後面的函數中有異步操作,await表示等待一個異步方法執行完成
聲明異步函數只要在普通函數前加上一個關鍵字async即可
async函數返回一個Promise對象,(若返回值不是Promise對象也會通過Promise.resolve() 封裝成 Promise 對象返回)因此async函數return返回的值可以通過then方法來接收
async function funcA() {
return 'hello!';
}
funcA().then(value => {
console.log(value);
})
// hello!
await就是異步等待,等待的是一個Promise,因此await後面應該是一個Promise對象,若不是,也會被轉成立即resolve的Promise
async函數被調用後就會開始執行,遇到await後就會等待其後面的異步操作執行完成,接着執行函數體後面的語句
總的來說:async函數調用不會造成代碼的阻塞,但是await會造成async函數內部代碼的阻塞
async function func() { console.log('async function is running!'); const num1 = await 200; console.log(`num1 is ${num1}`); const num2 = await num1+ 100; console.log(`num2 is ${num2}`); const num3 = await num2 + 100; console.log(`num3 is ${num3}`); } func(); console.log('run me before await!'); // async function is running! // run me before await! // num1 is 200 // num2 is 300 // num3 is 400
func函數執行後先輸出了 『async function is running!』,接着遇到了await異步等待,函數返回執行後面的同步任務 ‘run me before await!’
同步執行完成後接着await等待的位置繼續執行。
可以說:async函數可以看作多個異步任務包裝成一個Promise對象,而await命令就是其內部的語法糖
await後面的Promise對象不會總是返回resolved狀態,只要一個await後面的Promsie狀態變成rejected,整個async都會中斷執行
為了避免此情況可以使用try…catch來封裝多個await:
async function func() { try { const num1 = await 200; console.log(`num1 is ${num1}`); const num2 = await Promise.reject('num2 is wrong!'); console.log(`num2 is ${num2}`); const num3 = await num2 + 100; console.log(`num3 is ${num3}`); } catch (error) { console.log(error); } } func(); // num1 is 200 // 出錯了 // num2 is wrong!
async/await:使得異步代碼看起來像同步代碼
function sayHi(name) { return new Promise((resolved, rejected) => { setTimeout(() => { resolved(name); }, 2000) }) } async function sayHi_async(name) { const sayHi_1 = await sayHi(name) console.log(`你好, ${sayHi_1}`) const sayHi_2 = await sayHi('李四') console.log(`你好, ${sayHi_2}`) const sayHi_3 = await sayHi('王二麻子') console.log(`你好, ${sayHi_3}`) } sayHi_async('張三') // 你好, 張三 // 你好, 李四 // 你好, 王二麻子
Generator:
generator(生成器)是ES6標準引入的新的數據類型。一個generator看起來像個函數,但可以返回多次。
ES6定義的generator是借鑒了python中的generator概念和語法
函數的概念:一個函數是一段完整的代碼,調用一個函數就是傳入函數,然後返回結果
函數在執行的過程中,如果沒有遇到return語句(沒有return,就是隱含return undefined),控制權無法交回給被調用的代碼
function foo(x) { return x + x; } var r = foo(1); // 調用foo函數
而generator定義如下:
function* foo(x) { yield x + 1; yield x + 2; return x + 3; }
generator由 function* 定義,並且除了return語句外可以通過 yield 返回多次
舉個例子:斐波那契數列
function* fib(max) { var t, a = 0, b = 1, n = 0; while (n < max) { yield a; [a, b] = [b, a + b]; n ++; } return; }
調用generator和函數不同,有兩種調用方法,一是調用generator的 next() 方法:
var f = fib(5); f.next(); // {value: 0, done: false} f.next(); // {value: 1, done: false} f.next(); // {value: 1, done: false} f.next(); // {value: 2, done: false} f.next(); // {value: 3, done: false} f.next(); // {value: undefined, done: true}
next()方法會執行generator代碼,每次遇到 yield a;就會返回一個對象{value: a, done: false}。每次返回的value就是yield的返回值,done表示這個generator是否已經執行結束
二是直接使用 for…of 循環迭代generator
for (var x of fib(10)) { console.log(x); // 依次輸出0, 1, 1, 2, 3, ... }
generator的用處:
由於generator在執行的過程中可以返回多次,所以他看上去像一個可以記住執行狀態的函數,利用這一點,通過generator可以實現需要用面向對象才能實現的功能
generator還可以把異步代碼變成 「同步」 代碼
try { r1 = yield ajax('//url-1', data1); r2 = yield ajax('//url-2', data2); r3 = yield ajax('//url-3', data3); success(r3); } catch (err) { handle(err); }
new關鍵字的執行過程:(構造函數為Func)
- 創建一個空對象,並將該空對象繼承Func.prototype
- 執行構造函數,並將this指向剛剛創建的新對象
- 返回新對象
參考:
//segmentfault.com/a/1190000015488033
//www.liaoxuefeng.com/wiki/1022910821149312/1023024381818112#0