Promise是什麼:
Promise是非同步微任務(process.nextTick、Promise.then() catch() finally()等)
用於解決非同步多層巢狀回撥的問題(回撥地獄--小球運動),讓程式碼的可讀性更高、更容易維護
小球運動:一個小球元素使其按照右下左上的路徑運動,點選後觸發回撥函式,如果不使用Promise那麼就要層層巢狀回撥函式
運動到右後 再到下 再到左 再到上,而此時想要修改運動路徑的話,修改這一層又一層的程式碼會非常繁瑣,而Promise的then()鏈就可以很好的解決這個問題
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('http://url-1', data1); r2 = yield ajax('http://url-2', data2); r3 = yield ajax('http://url-3', data3); success(r3); } catch (err) { handle(err); }
new關鍵字的執行過程:(建構函式為Func)
- 建立一個空物件,並將該空物件繼承Func.prototype
- 執行建構函式,並將this指向剛剛建立的新物件
- 返回新物件
參考:
https://segmentfault.com/a/1190000015488033
https://www.liaoxuefeng.com/wiki/1022910821149312/1023024381818112#0