“1”基礎,自定義promise,你心動了嗎?
不論是面向就業還是正在學習前端的小夥伴,promise絕對是繞不過的知識點
正所謂學習一個技能的最好辦法是自己創造一個一樣的技能,甚至超越(瞎編的,但确實是這樣)
我們在學習promise的時候,應該需要注意很多問題,如果不了解這些,那promise整個運作過程,我敢說你絕對不會清晰,但是……但是……,看完我這篇文章,讓你明明白白入坑
promise^ - ^
1、promise你了解多少?
- 抽象表達:
期約是對尚不存在結果的一個替身,而Promise 是一門新的技術(ES6 規範),是 JS 中進行異步程式設計的新解決方案
- 具體表達:
- 從文法上來說: Promise 是一個構造函數
- 從功能上來說: Promise 對象用來封裝一個異步操作并可以擷取其 成功 / 失敗 的結果值
2、promise的狀态
初始狀态為pending,表示正在進行或者即将完成(不确定的狀态)
- pending 變為 fulfilled(期約已完成,成功)
- pending 變為 rejected(期約已完成,失敗)
隻有這 2 種狀态改變過程, 且一個 promise 對象隻能改變一次,後續不能更改一個已經完成的promise狀态,無論變為成功還是失敗, 都會有一個結果資料
3、使用promise有什麼好處?
- 指定回調函數的方式更靈活
之前是必須在啟動異步之前指定,而promise是:啟動異步任務——>傳回promise對象——>給promise綁定回調函數,而且可以在異步任務結束後指定多個
- 支援鍊式調用,可以解決回調地獄問題
回調地獄是什麼?——回調函數嵌套調用,外部回調函數異步執行的結果是嵌套的回調函數執行的條件。回調地獄會帶來什麼問題?——如果複雜的嵌套回調,代碼會無限想右延伸,代碼風格十分不友好,不便于閱讀,其次,多層嵌套不便于維護
下面實際看一下回調地獄和如何解決回調地獄
//回調地獄
func1(function(result) {
func2(result, function(newResult) {
func3(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult)
func4············
}, failureCallback)
}, failureCallback)
}, failureCallback)
//使用promise解決回調地獄
func1().then(function(result) {
return doSomethingElse(result)
}).then(function(newResult) {
return doThirdThing(newResult)
}).then(function(finalResult) {
console.log('Got the final result: ' + finalResult)
}).catch(failureCallback)
4、怎麼使用 promise ?
- Promise構造函數:Promise(executor){ }
其中executor是執行器,在promise内部立即同步調用,異步操作在執行器中執行
- Promise.prototype.then( function one ( ) { } , function two ( ) { } )方法:
成功則執行第一個函數,失敗則執行第二個函數,無論執行哪個函數,都傳回一個新的promise對象(自定義promise中的關鍵問題)
- Promise.prototype.catch( function one ( ) { } )方法
then方法的文法糖,相當于then( undefined , function two ( ) { } )
- Promise.resolve ( ) 方法
一般用于生成一個成功狀态的promise對象
- Promise.reject ( ) 方法
傳回一個失敗狀态的promise對象
- Promise.all( [ p1, p2, p3 , ··] )方法
數組内的元素都是promise對象,該方法傳回一個新的promise對象,隻有數組中的所有的promise都變成成功狀态,新的promise的狀态才是成功
- Promise.race( [ p1, p2, p3 , ··] ) 方法
數組内的元素都是promise對象,該方法傳回一個新的promise對象,數組中隻要有一個promise的狀态為成功,則新的promise則是成功,并且最終的新的promise的狀态和結果由數組中第一個變為成功的promise對象決定
- Promise.prototype.done( ) 方法
Promise 對象的回調鍊,不管以then方法或catch方法結尾,要是最後一個方法抛出錯誤,都有可能無法捕捉到(因為 Promise 内部的錯誤不會冒泡到全局)。是以,es6提供一個done方法,總是處于回調鍊的尾端,保證抛出任何可能出現的錯誤
- Promise.prototype.finally( ) 方法
用于指定不管 Promise 對象最後狀态如何,都會執行的操作。它與done方法的最大差別,它接受一個普通的回調函數作為參數,該函數不管怎樣都必須執行。
5、promise的幾個關鍵問題
- 怎麼改變promise的狀态?
調用resolve函數,pending——>fulfilled,調用reject函數,pending——>rejected,抛出異常,pending——>rejected
- 一個promise指定多個成功或者失敗的回調,都會調用嗎?
隻要當promise變成相應狀态時就會調用
- 改變promise狀态和指定回調函數誰先誰後?
(1) 都有可能, 正常情況下是先指定回調再改變狀态, 但也可以先改狀态再指定回調
(2) 如何先改狀态再指定回調?
① 在執行器中直接調用 resolve()/reject()
② 延遲更長時間才調用 then()
(3) 什麼時候才能得到資料?
① 如果先指定的回調, 那當狀态發生改變時, 回調函數就會調用, 得到資料
② 如果先改變的狀态, 那當指定回調時, 回調函數就會調用, 得到資料
- promise.then()傳回的新 promise 的結果狀态由什麼決定?
(1) 簡單表達: 由 then()指定的回調函數執行的結果決定
(2) 詳細表達:
① 如果抛出異常, 新 promise 變為 rejected, reason 為抛出的異常
② 如果傳回的是非promise的任意值, 新promise變為resolved, value為傳回的值
③ 如果傳回的是另一個新promise, 此promise的結果就會成為新promise的結果
- promise 如何串連多個操作任務?
(1) promise 的 then()傳回一個新的 promise, 可以開成 then()的鍊式調用
(2) 通過 then 的鍊式調用串連多個同步/異步任務
- promise 異常傳透是什麼,怎麼解決?
(1) 當使用 promise 的 then 鍊式調用時, 可以在最後指定失敗的回調,
(2) 前面任何操作出了異常, 都會傳到最後失敗的回調中處理
- 如何中斷 promise 鍊?
(1) 當使用 promise 的 then 鍊式調用時, 在中間中斷, 不再調用後面的回調函數
(2) 辦法: 在回調函數中傳回一個 pendding 狀态的 promise 對象
到了激動人心的時刻了,怎麼自定義一個promise呢?
根據上面提出的問題(需要注意的地方),我們就能将自己的promise封裝的完整一些了
我們一起來淦吧!
class MYPromise {//我的類名,區分Promise
//構造函數
constructor(executor) {
this.myPromiseState = "pending";
this.myPromiseResult = null;
//當狀态時pending時儲存執行成功和失敗的回調函數,等待狀态改變
this.callbacks = [];//鍊式調用時,需要儲存回調函數
const _this = this;
function resolve(data) {
if (_this.myPromiseState !== "pending") return;//對應狀态一旦改變,promise的狀态就不能改變
_this.myPromiseState = "fulfilled";
_this.myPromiseResult = data;
//内部異步改變狀态時調用
setTimeout(() => {
_this.callbacks.forEach(item => {
item.onResolved(data);
})
}, 0);
}
function reject(data) {
if (_this.myPromiseState !== "pending") return;
_this.myPromiseState = "rejected";
_this.myPromiseResult = data;
//内部異步改變狀态時調用
setTimeout(() => {
_this.callbacks.forEach(item => {
item.onRejected(data);
})
}, 0);
}
try {//抛出異常,調用reject,并且狀态變成失敗
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// then 方法
then(onResolved, onRejected) {
const _this = this;
// 判斷參數,處理catch異常穿透功能
if (typeof onRejected !== 'function') {
onRejected = reason => {
throw reason;
}
}
if (typeof onResolved !== 'function') {
onResolved = value => value
}
return new MYPromise((resolve, reject) => {
// 封裝try catch函數
function callback(type) {
try {
let result = type(_this.myPromiseResult);
// 判斷是不是promise對象
if (result instanceof MYPromise) {
//如果是一個promise對象
result.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
}
if (this.myPromiseState === "fulfilled") {
setTimeout(() => {
callback(onResolved);
}, 0);
}
if (this.myPromiseState === "rejected") {
setTimeout(() => {
callback(onRejected);
}, 0);
}
if (this.myPromiseState === "pending") {
this.callbacks.push({
onResolved: function() {
callback(onResolved);
},
onRejected: function() {
callback(onRejected);
}
});
}
})
}
// catch方法
catch (onRejected) {
return this.then(undefined, onRejected);
}
// resolve 方法
static resolve(value) {
return new MYPromise((resolve, reject) => {
if (value instanceof MYPromise) {
value.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
resolve(value);
}
})
}
// reject 方法
static reject(reason) {
return new MYPromise((resolve, reject) => {
reject(reason);
})
}
// all 方法
static all(promises) {
return new MYPromise((resolve, reject) => {
// 維護一個變量,表示成功的個數
let count = 0;
// 維護一個數組,存放成功的promise的值
let arr = [];
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
count++;
arr[i] = v;
if (count === promises.length) {//全部成功,新promise狀态才會成功
resolve(arr);
}
}, r => {
reject(r);
})
}
})
}
// race 方法
static race(promises) {
return new MYPromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
resolve(v);//有一個成功,則新promise狀态為成功,且由數組中第一個成功的promise決定
}, r => {
reject(r);
})
}
})
}
}
//使用我自定義的promise
let p1 = new MYPromise((a, b) => {//使用a,b是為了說明這裡的函數名不是決定性因素,不要被迷惑,真正起作用的是構造函數裡面的resolve和reject函數
console.log(1111)
// a("ok")
// b('ok');
setTimeout(() => {
// a("ok")
b("err")
}, 0);
});
p1.then(v => {
console.log(v);
}, r => {
console.warn(r);
})
好了,淦完了,我相信大家一定都會了😜😜😜
最後一句,祝大家天天開心!