官方文檔地址 https://promisesaplus.com/
本文不涉及具體用法冕杠,只從具體實現(xiàn)上出發(fā)
第一步钠糊,實現(xiàn)同步版的promise
promise包含幾個關(guān)鍵詞:
- resolve
- reject
- then
其中resolve和reject的代碼在正常使用的時候是看不到的俺亮,但可以猜測他們兩個都應(yīng)該是回調(diào)函數(shù)戚炫,傳遞給了用戶傳入的函數(shù)扬跋,而then則掛在原型上
結(jié)構(gòu)如下:
calss Promise{
consturctor(exector){
function resolve(){
}
function reject(){
}
exector(resolve,reject)
}
then(){
}
}
文檔中提出园细,promise具備三種邏輯判斷狀態(tài)pending、fulfilled昔馋、rejected,三者不并處糖耸,同一時間只能存在一種狀態(tài)
pending向fulfilled或rejected單向流動秘遏。
同步版本實現(xiàn)
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise{
constructor(exector){
let self = this;
self.status = PENDING;
self.value = undefined;
self.reason = undefined;
let resolve = (value)=>{
if(self.status === PENDING){
self.status = FULFILLED
self.value = value;
}
}
let reject = (reason)=>{
if(self.status === PENDING){
self.status = REJECTED;
self.reason = reason
}
}
try{
exector(resolve,reject)
}catch(e){
reject(e)
}
}
then(onFulfilled,onRejected){
let self = this;
if(self.status === FULFILLED){
onFulfilled(self.value);
}
if(self.status === REJECTED){
onRejected(self.reason);
}
}
};
現(xiàn)在測試一下:
new Promise((resolve,reject)=>{
reject("1");
}).then((data)=>{
console.log(data)
},(reason)=>{
console.log(reason)
})
resolve和reject都能夠得到準確輸出
存在的問題
異步不支持
new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(2)
})
}).then((data)=>{
console.log(data)
},(reason)=>{
console.log(reason)
})
現(xiàn)在用setTimeout包裹resolve,結(jié)果就是什么輸出也沒有
同步的話嘉竟,resolve先執(zhí)行邦危,onFulfilled后執(zhí)行,此時狀態(tài)已經(jīng)變成了fulfilled
異步的話舍扰,then先執(zhí)行倦蚪,此時status還是pending,無法進入fulfilled狀態(tài)边苹,所以onFulfilled不會執(zhí)行陵且,setTimeout之后resolve改變status,但已經(jīng)找不到onFulfilled了
then沒有容錯
new Promise((resolve,reject)=>{
resolve(2)
}).then()
像上面這種情況个束,如果沒有給then方法傳遞參數(shù)慕购,那么程序會報錯
解決存在的問題
異步promise
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise{
constructor(exector){
let self = this;
self.status = PENDING;
self.value = undefined;
self.reason = undefined;
self.onResolveCallBacks = [];
self.onRejectCallBacks = [];
let resolve = (value)=>{
if(self.status === PENDING){
self.status = FULFILLED
self.value = value;
self.onResolveCallBacks.forEach(cb=>cb(self.value));
}
}
let reject = (reason)=>{
if(self.status === PENDING){
self.status = REJECTED;
self.reason = reason
self.onRejectCallBacks.forEach(cb=>cb(self.reason));
}
}
try{
exector(resolve,reject)
}catch(e){
reject(e)
}
}
then(onFulfilled,onRejected){
let self = this;
if(self.status === FULFILLED){
onFulfilled(self.value);
}
if(self.status === REJECTED){
onRejected(self.reason);
}
if(self.status === PENDING){
self.onResolveCallBacks.push(onFulfilled);
self.onRejectCallBacks.push(onRejected);
}
}
};
因為resolve和then的執(zhí)行順序無法保證,所以要用訂閱發(fā)布的方式來實現(xiàn)茬底,所以創(chuàng)建兩個數(shù)組沪悲,分別存儲成功和失敗的回調(diào)函數(shù)
self.onResolveCallBacks = [];
self.onRejectCallBacks = [];
然后再改寫resolve和reject函數(shù),執(zhí)行發(fā)布
let resolve = (value)=>{
if(self.status === PENDING){
self.status = FULFILLED
self.value = value;
self.onResolveCallBacks.forEach(cb=>cb(self.value));
}
}
此時進行測試:
new Promise((resolve,reject)=>{
setTimeout(()=>{
reject("失敗")
},2000)
}).then((data)=>{
console.log(data);
},(reason)=>{
console.log(reason);
})
成功輸出失敗
阱表,到此異步的promise完成了
then容錯
總錯這里不太好理解殿如,所以后面再做
第二步,Promise的鏈式調(diào)用
promise支持鏈式調(diào)用最爬,使用方法如:
new Promise((resolve,reject)=>{
//resolve or reject
}).then((d)=>{
return d
},(r)=>{
return r
}).then((d)=>{
return d
},(r)=>{
return r
}).then((data)=>{
console.log(data);
},(reason)=>{
console.log(reason);
})
上面的.then中return后再then就是鏈式調(diào)用
前面知道promise實例具備then方法涉馁,所以如果我們的then方法執(zhí)行后,再返回一個Promise方法的話爱致,不就可以繼續(xù)進行.then了嗎谨胞?
同步鏈式調(diào)用
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise{
constructor(exector){
let self = this;
self.status = PENDING;
self.value = undefined;
self.reason = undefined;
self.onResolveCallBacks = [];
self.onRejectCallBacks = [];
let resolve = (value)=>{
if(self.status === PENDING){
self.status = FULFILLED
self.value = value;
self.onResolveCallBacks.forEach(cb=>cb(self.value));
}
}
let reject = (reason)=>{
if(self.status === PENDING){
self.status = REJECTED;
self.reason = reason
self.onRejectCallBacks.forEach(cb=>cb(self.reason));
}
}
try{
exector(resolve,reject)
}catch(e){
reject(e)
}
}
then(onFulfilled,onRejected){
// onFulfilled = typeof onFulfilled == 'function'?onFulfilled:value=>value;
// onRejected = typeof onRejected == 'function'?onRejected:reason => {throw reason};
let self = this;
if(self.status === FULFILLED){
return new Promise((resolve,reject)=>{
let x = onFulfilled(self.value);
resolve(x);
})
}
if(self.status === REJECTED){
return new Promise((resolve,reject)=>{
let x = onRejected(self.reason);
reject(x);
})
}
if(self.status === PENDING){
self.onResolveCallBacks.push(onFulfilled);
self.onRejectCallBacks.push(onRejected);
}
}
};
這里核心就是then方法返回了一個新的promise實例:
return new Promise((resolve,reject)=>{
let x = onFulfilled(self.value);
resolve(x);
})
1,pormise(p1)執(zhí)行resolve蒜鸡,改變status為fulfilled
2胯努,then方法執(zhí)行onFulfilled
3牢裳,onFulfilled返回一個新的promise,簡稱p2
4叶沛,由于p1是resolve蒲讯,所以p2也知行resolve
5,p2執(zhí)行灰署,改變了p2的status為fulfilled
6判帮,p2繼續(xù)執(zhí)行p2的onFulfilled
至此就做到了一個同步的鏈式調(diào)用
異步鏈式調(diào)用
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise{
constructor(exector){
let self = this;
self.status = PENDING;
self.value = undefined;
self.reason = undefined;
self.onResolveCallBacks = [];
self.onRejectCallBacks = [];
let resolve = (value)=>{
if(self.status === PENDING){
self.status = FULFILLED
self.value = value;
self.onResolveCallBacks.forEach(cb=>cb(self.value));
}
}
let reject = (reason)=>{
if(self.status === PENDING){
self.status = REJECTED;
self.reason = reason
self.onRejectCallBacks.forEach(cb=>cb(self.reason));
}
}
try{
exector(resolve,reject)
}catch(e){
reject(e)
}
}
then(onFulfilled,onRejected){
let self = this;
if(self.status === FULFILLED){
return new Promise((resolve,reject)=>{
let x = onFulfilled(self.value);
resolve(x);
})
}
if(self.status === REJECTED){
return new Promise((resolve,reject)=>{
let x = onRejected(self.reason);
reject(x);
})
}
if(self.status === PENDING){
return new Promise(function(resolve,reject){
self.onResolveCallBacks.push(function(){
let x = onFulfilled(self.value);
resolve(x);
});
self.onRejectCallBacks.push(function(){
let x = onRejected(self.reason);
reject(x);
});
})
}
}
};
這里的核心是當status為pending狀態(tài)時,同樣要立刻
返回一個promise對象溉箕,否則沒有返回值的話晦墙,第二次鏈式調(diào)用的then方法根本不存在,需要仔細理解這里
if(self.status === PENDING){
return new Promise(function(resolve,reject){
self.onResolveCallBacks.push(function(){
let x = onFulfilled(self.value);
resolve(x);
});
self.onRejectCallBacks.push(function(){
let x = onRejected(self.reason);
reject(x);
});
})
}
第三步肴茄,鏈式調(diào)用其他的promise實現(xiàn)
經(jīng)過前面晌畅,我們已經(jīng)實現(xiàn)了一個鏈式調(diào)用的promise,但還存在一種情況寡痰,就是如果第一個onFulfilled返回一個Promise對像的話怎么處理
new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(new Promise((resolve,reject)=>{
resolve("10000");
}))
},3000)
}).then((d1)=>{
console.log(d1);
return d1
},(r1)=>{
console.log(r1);
return r1;
}).then((d2)=>{
console.log(d2);
},(r2)=>{
console.log(r2);
})
例如上面的情況抗楔,拿到的結(jié)果就出現(xiàn)了問題
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise{
constructor(exector){
let self = this;
self.status = PENDING;
self.value = undefined;
self.reason = undefined;
self.onResolveCallBacks = [];
self.onRejectCallBacks = [];
let resolve = (value)=>{
if(self.status === PENDING){
self.status = FULFILLED
self.value = value;
self.onResolveCallBacks.forEach(cb=>cb(self.value));
}
}
let reject = (reason)=>{
if(self.status === PENDING){
self.status = REJECTED;
self.reason = reason
self.onRejectCallBacks.forEach(cb=>cb(self.reason));
}
}
try{
exector(resolve,reject)
}catch(e){
reject(e)
}
}
then(onFulfilled,onRejected){
// onFulfilled = typeof onFulfilled == 'function'?onFulfilled:value=>value;
// onRejected = typeof onRejected == 'function'?onRejected:reason => {throw reason};
let self = this;
if(self.status === FULFILLED){
return new Promise((resolve,reject)=>{
let x = onFulfilled(self.value);
resolve(x);
})
}
if(self.status === REJECTED){
return new Promise((resolve,reject)=>{
let x = onRejected(self.reason);
reject(x);
})
}
if(self.status === PENDING){
return new Promise(function(resolve,reject){
self.onResolveCallBacks.push(function(){
let x = onFulfilled(self.value);
if(x instanceof Promise){
x.then(resolve,reject)
}else{
resolve(x);
}
});
self.onRejectCallBacks.push(function(){
let x = onRejected(self.reason);
if(x instanceof Promise){
x.then(resolve,reject);
}else{
reject(x);
}
});
})
}
}
};
這一次關(guān)鍵的代碼是
return new Promise(function(resolve,reject){
self.onResolveCallBacks.push(function(){
let x = onFulfilled(self.value);
if(x instanceof Promise){
x.then(resolve,reject)
}else{
resolve(x);
}
});
self.onRejectCallBacks.push(function(){
let x = onRejected(self.reason);
if(x instanceof Promise){
x.then(resolve,reject);
}else{
reject(x);
}
});
})
1,通過instanceof判斷了x是否是promise對象
2拦坠,如果x是promise對象连躏,稱為p3
3,將p2的resolve當作p3的onFulfilled
4贞滨,當p3resolve的時候入热,實際之行的是p2的resolve
5,p2的resolve執(zhí)行的時候晓铆,會執(zhí)行第二次的then方法中添加進來的onFulfilled
繼續(xù)完善鏈式調(diào)用
new Promise((resolve,reject)=>{
// setTimeout(()=>{
resolve("成功")
// },3000)/
}).then((d1)=>{
console.log(d1);
return new Promise((resolve,reject)=>{
resolve(new Promise((resolve,reject)=>{
resolve("111")
}));
})
},(r1)=>{
console.log(r1);
return r1;
}).then((d2)=>{
console.log(d2);
},(r2)=>{
console.log(r2);
})
例如上面這種才顿,多層promise對象不斷返回的場景
之前的寫法只能滿足一層需求
所以需要使用遞歸來實現(xiàn),除此之外還要考慮邊界問題尤蒿,至于邊界可以對照文檔來逐步實現(xiàn),全部實現(xiàn)代碼如下:
const PENDING = 'pending';//初始態(tài)
const FULFILLED = 'fulfilled';//初始態(tài)
const REJECTED = 'rejected';//初始態(tài)
function Promise(executor){
let self = this;//先緩存當前promise實例
self.status = PENDING;//設(shè)置狀態(tài)
//定義存放成功的回調(diào)的數(shù)組
self.onResolvedCallbacks = [];
//定義存放失敗回調(diào)的數(shù)組
self.onRejectedCallbacks = [];
//當調(diào)用此方法的時候郑气,如果promise狀態(tài)為pending,的話可以轉(zhuǎn)成成功態(tài),如果已經(jīng)是成功態(tài)或者失敗態(tài)了,則什么都不做
//2.1
function resolve(value){ //2.1.1
if(value!=null &&value.then&&typeof value.then == 'function'){
return value.then(resolve,reject);
}
//如果是初始態(tài)腰池,則轉(zhuǎn)成成功態(tài)
//為什么要把它用setTimeout包起來
setTimeout(function(){
if(self.status == PENDING){
self.status = FULFILLED;
self.value = value;//成功后會得到一個值尾组,這個值不能改
//調(diào)用所有成功的回調(diào)
self.onResolvedCallbacks.forEach(cb=>cb(self.value));
}
})
}
function reject(reason){ //2.1.2
setTimeout(function(){
//如果是初始態(tài),則轉(zhuǎn)成失敗態(tài)
if(self.status == PENDING){
self.status = REJECTED;
self.value = reason;//失敗的原因給了value
self.onRejectedCallbacks.forEach(cb=>cb(self.value));
}
});
}
try{
//因為此函數(shù)執(zhí)行可能會異常示弓,所以需要捕獲讳侨,如果出錯了,需要用錯誤 對象reject
executor(resolve,reject);
}catch(e){
//如果這函數(shù)執(zhí)行失敗了奏属,則用失敗的原因reject這個promise
reject(e);
};
}
function resolvePromise(promise2,x,resolve,reject){
if(promise2 === x){
return reject(new TypeError('循環(huán)引用'));
}
let called = false;//promise2是否已經(jīng)resolve 或reject了
if(x instanceof Promise){
if(x.status == PENDING){
x.then(function(y){
resolvePromise(promise2,y,resolve,reject);
},reject);
}else{
x.then(resolve,reject);
}
//x是一個thenable對象或函數(shù)跨跨,只要有then方法的對象,
}else if(x!= null &&((typeof x=='object')||(typeof x == 'function'))){
//當我們的promise和別的promise進行交互,編寫這段代碼的時候盡量的考慮兼容性勇婴,允許別人瞎寫
try{
let then = x.then;
if(typeof then == 'function'){
//有些promise會同時執(zhí)行成功和失敗的回調(diào)
then.call(x,function(y){
//如果promise2已經(jīng)成功或失敗了忱嘹,則不會再處理了
if(called)return;
called = true;
resolvePromise(promise2,y,resolve,reject)
},function(err){
if(called)return;
called = true;
reject(err);
});
}else{
//到此的話x不是一個thenable對象,那直接把它當成值resolve promise2就可以了
resolve(x);
}
}catch(e){
if(called)return;
called = true;
reject(e);
}
}else{
//如果X是一個普通 的值耕渴,則用x的值去resolve promise2
resolve(x);
}
}
//onFulfilled 是用來接收promise成功的值或者失敗的原因
Promise.prototype.then = function(onFulfilled,onRejected){
//如果成功和失敗的回調(diào)沒有傳拘悦,則表示這個then沒有任何邏輯,只會把值往后拋
//2.2.1
onFulfilled = typeof onFulfilled == 'function'?onFulfilled:function(value){return value};
onRejected = typeof onRejected == 'function'?onRejected:reason=>{throw reason};
//如果當前promise狀態(tài)已經(jīng)是成功態(tài)了橱脸,onFulfilled直接取值
let self = this;
let promise2;
if(self.status == FULFILLED){
return promise2 = new Promise(function(resolve,reject){
setTimeout(function(){
try{
let x =onFulfilled(self.value);
//如果獲取到了返回值x,會走解析promise的過程
resolvePromise(promise2,x,resolve,reject);
}catch(e){
//如果執(zhí)行成功的回調(diào)過程中出錯了础米,用錯誤原因把promise2 reject
reject(e);
}
})
});
}
if(self.status == REJECTED){
return promise2 = new Promise(function(resolve,reject){
setTimeout(function(){
try{
let x =onRejected(self.value);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
})
});
}
if(self.status == PENDING){
return promise2 = new Promise(function(resolve,reject){
self.onResolvedCallbacks.push(function(){
try{
let x =onFulfilled(self.value);
//如果獲取到了返回值x,會走解析promise的過程
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
});
self.onRejectedCallbacks.push(function(){
try{
let x =onRejected(self.value);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
});
});
}
}
module.exports = Promise;
主要是通過resolvePromise方法將具體的遞歸,以及邊界問題全部處理完畢
第四步添诉,promise api實現(xiàn)
Promise.all
接收一個數(shù)組屁桑,全部成功后才返回
Promise.all = function(arr){
return new Promise((resolve,reject)=>{
let resolvList=[];
arr.forEach((item)=>{
item.then((data)=>{
resolvList.push(data);
console.log(data);
if(arr.length == resolvList.length){
resolve(resolvList);
}
},(reason)=>{
reject(reason);
})
})
})
}
Promise.race
接受一個數(shù)組,一個成功即返回
Promise.race = function(arr){
return new Promise((resolve,reject)=>{
arr.forEach((item)=>{
item.then((data)=>{
resolve(data);
},(reason)=>{
reject(reason);
})
})
})
}
Promise.resolve
立刻返回一個promise對象栏赴,一般用于沒有promise對象蘑斧,需要將一個東西,轉(zhuǎn)為promise
Promise.resolve = function(value){
return new Promise(function(resolve){
resolve(value);
});
}
Promise.reject
立刻返回一個promise對象艾帐,一般用于沒有promise對象,需要將一個東西盆偿,轉(zhuǎn)為promise
Promise.reject = function(reason){
return new Promise(function(resolve,reject){
reject(reason);
});
}
終于把這篇寫個大概了柒爸,待過一段時間再回顧一遍這個知識,加油事扭!