JS手搓P(guān)romise的常見方法總結(jié)
1. 手搓 Promise
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
function MyPromise(fn) {
// 保存初始化狀態(tài)
var self = this;
// 初始化狀態(tài)
this.state = PENDING;
// 用于保存 resolve 或者 rejected 傳入的值
this.value = null;
// 用于保存 resolve 的回調(diào)函數(shù)
this.resolvedCallbacks = [];
// 用于保存 reject 的回調(diào)函數(shù)
this.rejectedCallbacks = [];
// 狀態(tài)轉(zhuǎn)變?yōu)?resolved 方法
function resolve(value) {
// 判斷傳入元素是否為 Promise 值,如果是,則狀態(tài)改變必須等待前一個(gè)狀態(tài)改變后再進(jìn)行改變
if (value instanceof MyPromise) {
return value.then(resolve, reject);
}
// 保證代碼的執(zhí)行順序?yàn)楸据喪录h(huán)的末尾
setTimeout(() => {
// 只有狀態(tài)為 pending 時(shí)才能轉(zhuǎn)變,
if (self.state === PENDING) {
// 修改狀態(tài)
self.state = RESOLVED;
// 設(shè)置傳入的值
self.value = value;
// 執(zhí)行回調(diào)函數(shù)
self.resolvedCallbacks.forEach(callback => {
callback(value);
});
}
}, 0);
}
// 狀態(tài)轉(zhuǎn)變?yōu)?rejected 方法
function reject(value) {
// 保證代碼的執(zhí)行順序?yàn)楸据喪录h(huán)的末尾
setTimeout(() => {
// 只有狀態(tài)為 pending 時(shí)才能轉(zhuǎn)變
if (self.state === PENDING) {
// 修改狀態(tài)
self.state = REJECTED;
// 設(shè)置傳入的值
self.value = value;
// 執(zhí)行回調(diào)函數(shù)
self.rejectedCallbacks.forEach(callback => {
callback(value);
});
}
}, 0);
}
// 將兩個(gè)方法傳入函數(shù)執(zhí)行
try {
fn(resolve, reject);
} catch (e) {
// 遇到錯(cuò)誤時(shí),捕獲錯(cuò)誤,執(zhí)行 reject 函數(shù)
reject(e);
}
}
MyPromise.prototype.then = function(onResolved, onRejected) {
// 首先判斷兩個(gè)參數(shù)是否為函數(shù)類型,因?yàn)檫@兩個(gè)參數(shù)是可選參數(shù)
onResolved =
typeof onResolved === "function"
? onResolved
: function(value) {
return value;
};
onRejected =
typeof onRejected === "function"
? onRejected
: function(error) {
throw error;
};
// 如果是等待狀態(tài),則將函數(shù)加入對應(yīng)列表中
if (this.state === PENDING) {
this.resolvedCallbacks.push(onResolved);
this.rejectedCallbacks.push(onRejected);
}
// 如果狀態(tài)已經(jīng)凝固,則直接執(zhí)行對應(yīng)狀態(tài)的函數(shù)
if (this.state === RESOLVED) {
onResolved(this.value);
}
if (this.state === REJECTED) {
onRejected(this.value);
}
};2. 手搓 Promise.then
then 方法返回一個(gè)新的 promise 實(shí)例,為了在 promise 狀態(tài)發(fā)生變化時(shí)(resolve / reject 被調(diào)用時(shí))再執(zhí)行 then 里的函數(shù),我們使用一個(gè) callbacks 數(shù)組先把傳給then的函數(shù)暫存起來,等狀態(tài)改變時(shí)再調(diào)用。
那么,怎么保證后一個(gè) **then** 里的方法在前一個(gè) **then**(可能是異步)結(jié)束之后再執(zhí)行呢?
我們可以將傳給 then 的函數(shù)和新 promise 的 resolve 一起 push 到前一個(gè) promise 的 callbacks 數(shù)組中,達(dá)到承前啟后的效果:
- 承前:當(dāng)前一個(gè)
promise完成后,調(diào)用其resolve變更狀態(tài),在這個(gè)resolve里會依次調(diào)用callbacks里的回調(diào),這樣就執(zhí)行了then里的方法了 - 啟后:上一步中,當(dāng)
then里的方法執(zhí)行完成后,返回一個(gè)結(jié)果,如果這個(gè)結(jié)果是個(gè)簡單的值,就直接調(diào)用新promise的resolve,讓其狀態(tài)變更,這又會依次調(diào)用新promise的callbacks數(shù)組里的方法,循環(huán)往復(fù)。。如果返回的結(jié)果是個(gè)promise,則需要等它完成之后再觸發(fā)新promise的resolve,所以可以在其結(jié)果的then里調(diào)用新promise的resolve
then(onFulfilled, onReject){
// 保存前一個(gè)promise的this
const self = this;
return new MyPromise((resolve, reject) => {
// 封裝前一個(gè)promise成功時(shí)執(zhí)行的函數(shù)
let fulfilled = () => {
try{
const result = onFulfilled(self.value); // 承前
return result instanceof MyPromise? result.then(resolve, reject) : resolve(result); //啟后
}catch(err){
reject(err)
}
}
// 封裝前一個(gè)promise失敗時(shí)執(zhí)行的函數(shù)
let rejected = () => {
try{
const result = onReject(self.reason);
return result instanceof MyPromise? result.then(resolve, reject) : reject(result);
}catch(err){
reject(err)
}
}
switch(self.status){
case PENDING:
self.onFulfilledCallbacks.push(fulfilled);
self.onRejectedCallbacks.push(rejected);
break;
case FULFILLED:
fulfilled();
break;
case REJECT:
rejected();
break;
}
})
}注意:
- 連續(xù)多個(gè)
then里的回調(diào)方法是同步注冊的,但注冊到了不同的callbacks數(shù)組中,因?yàn)槊看?nbsp;then都返回新的promise實(shí)例(參考上面的例子和圖) - 注冊完成后開始執(zhí)行構(gòu)造函數(shù)中的異步事件,異步完成之后依次調(diào)用
callbacks數(shù)組中提前注冊的回調(diào)
3. 手搓 Promise.all
1) 核心思路
- 接收一個(gè) Promise 實(shí)例的數(shù)組或具有 Iterator 接口的對象作為參數(shù)
- 這個(gè)方法返回一個(gè)新的 promise 對象,
- 遍歷傳入的參數(shù),用Promise.resolve()將參數(shù)"包一層",使其變成一個(gè)promise對象
- 參數(shù)所有回調(diào)成功才是成功,返回值數(shù)組與參數(shù)順序一致
- 參數(shù)數(shù)組其中一個(gè)失敗,則觸發(fā)失敗狀態(tài),第一個(gè)觸發(fā)失敗的 Promise 錯(cuò)誤信息作為 Promise.all 的錯(cuò)誤信息。
2)實(shí)現(xiàn)代碼
一般來說,Promise.all 用來處理多個(gè)并發(fā)請求,也是為了頁面數(shù)據(jù)構(gòu)造的方便,將一個(gè)頁面所用到的在不同接口的數(shù)據(jù)一起請求過來,不過,如果其中一個(gè)接口失敗了,多個(gè)請求也就失敗了,頁面可能啥也出不來,這就看當(dāng)前頁面的耦合程度了
function promiseAll(promises) {
return new Promise(function(resolve, reject) {
if(!Array.isArray(promises)){
throw new TypeError(`argument must be a array`)
}
var resolvedCounter = 0;
var promiseNum = promises.length;
var resolvedResult = [];
for (let i = 0; i < promiseNum; i++) {
Promise.resolve(promises[i]).then(value=>{
resolvedCounter++;
resolvedResult[i] = value;
if (resolvedCounter == promiseNum) {
return resolve(resolvedResult)
}
},error=>{
return reject(error)
})
}
})
}
// test
let p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(1)
}, 1000)
})
let p2 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(2)
}, 2000)
})
let p3 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(3)
}, 3000)
})
promiseAll([p3, p1, p2]).then(res => {
console.log(res) // [3, 1, 2]
})4. 手搓 Promise.race
該方法的參數(shù)是 Promise 實(shí)例數(shù)組, 然后其 then 注冊的回調(diào)方法是數(shù)組中的某一個(gè) Promise 的狀態(tài)變?yōu)?fulfilled 的時(shí)候就執(zhí)行. 因?yàn)?Promise 的狀態(tài)只能改變一次, 那么我們只需要把 Promise.race 中產(chǎn)生的 Promise 對象的 resolve 方法, 注入到數(shù)組中的每一個(gè) Promise 實(shí)例中的回調(diào)函數(shù)中即可.
Promise.race = function (args) {
return new Promise((resolve, reject) => {
for (let i = 0, len = args.length; i < len; i++) {
args[i].then(resolve, reject)
}
})
}到此這篇關(guān)于JS手搓P(guān)romise的常見方法總結(jié)的文章就介紹到這了,更多相關(guān)JS Promise內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家
相關(guān)文章
JS正則截取兩個(gè)字符串之間及字符串前后內(nèi)容的方法
這篇文章主要介紹了JS正則截取兩個(gè)字符串之間及字符串前后內(nèi)容的方法,結(jié)合實(shí)例形式簡單分析了JS正則截取字符串操作的常用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2017-01-01
uploadify在Firefox下丟失session問題的解決方法
在用uploadify上傳插件時(shí)遇到了一個(gè)問題,在讀session時(shí)認(rèn)為沒有權(quán)限而被攔截了,后來在后臺打印登錄時(shí)產(chǎn)生session的id和上傳時(shí)讀取session的id,解決方法如下,感興趣的朋友可以了解下2013-08-08
javascript數(shù)組中的reduce方法和pop方法
這篇文章主要介紹了javascript數(shù)組中的reduce方法和pop方法,文章內(nèi)容介紹詳細(xì),具有一定的參考價(jià)值需要的小伙伴可以參考一下,希望對你的學(xué)習(xí)有所幫助2022-03-03

