為了更更加好的理解promise ,簡(jiǎn)單的學(xué)習(xí)了一下手寫(xiě)promise。
于是我就簡(jiǎn)單的學(xué)了一下 腔呜,首先分步操作,
先實(shí)現(xiàn)基礎(chǔ)部分再悼,不考慮鏈?zhǔn)秸{(diào)用核畴,
function myPromise(executor) {
var _this = this;
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledFunc = [];//保存成功回調(diào)
this.onRejectedFunc = [];//保存失敗回調(diào)
//其它代碼略...
function resolve(value) {
//當(dāng)狀態(tài)為pending時(shí)再做更新
if (_this.state === 'pending') {
_this.value = value;//保存成功結(jié)果
_this.state = 'resolved';
}
}
function reject(reason) {
//當(dāng)狀態(tài)為pending時(shí)再做更新
if (_this.state === 'pending') {
_this.reason = reason;//保存失敗原因
_this.state = 'rejected';
}
}
//這里執(zhí)行promise傳入的函數(shù)部分
try {
executor(resolve, reject); //馬上執(zhí)行
}
catch (error) {
reject(error)
}
}
then方法的實(shí)現(xiàn)
當(dāng)myPromise的狀態(tài)發(fā)生了改變,不論是成功或是失敗都會(huì)調(diào)用then方法冲九,所以谤草,then方法的實(shí)現(xiàn)也很簡(jiǎn)單,根據(jù)state狀態(tài)來(lái)調(diào)用不同的回調(diào)函數(shù)即可,但是由于執(zhí)行的時(shí)候可能狀態(tài)還是pending 所以執(zhí)行的時(shí)候如果是pending 我們則把傳入的成功和失敗的回調(diào)函數(shù)傳入callback數(shù)組里面 等resolve函數(shù)里面去執(zhí)行
myPromise.prototype.then = function (onFulfilled, onRejected) {
//等待態(tài)莺奸,此時(shí)異步代碼還沒(méi)有走完
if (this.state === 'pending') {
if (typeof onFulfilled === 'function') {
this.onFulfilledFunc.push(onFulfilled);//保存回調(diào)
}
if (typeof onRejected === 'function') {
this.onRejectedFunc.push(onRejected);//保存回調(diào)
}
}
//其它代碼略...
}
鏈?zhǔn)秸{(diào)用
romise處理異步代碼最強(qiáng)大的地方就是支持鏈?zhǔn)秸{(diào)用丑孩,這塊也是最復(fù)雜的,我們先梳理一下規(guī)范中是怎么定義的:
每個(gè)then方法都返回一個(gè)新的Promise對(duì)象(原理的核心)
如果then方法中顯示地返回了一個(gè)Promise對(duì)象就以此對(duì)象為準(zhǔn)灭贷,返回它的結(jié)果
如果then方法中返回的是一個(gè)普通值(如Number温学、String等)就使用此值包裝成一個(gè)新的Promise對(duì)象返回。
如果then方法中沒(méi)有return語(yǔ)句甚疟,就視為返回一個(gè)用Undefined包裝的Promise對(duì)象
若then方法中出現(xiàn)異常仗岖,則調(diào)用失敗態(tài)方法(reject)跳轉(zhuǎn)到下一個(gè)then的onRejected
如果then方法沒(méi)有傳入任何回調(diào),則繼續(xù)向下傳遞(值的傳遞特性)古拴。
看起來(lái)抽象 代碼解釋看下面簡(jiǎn)書(shū)博客
resolve 和reject的改造:
對(duì)于 resolve 函數(shù)來(lái)說(shuō)箩帚,首先需要判斷傳入的值是否為 Promise 類(lèi)型
為了保證函數(shù)執(zhí)行順序,需要將兩個(gè)函數(shù)體代碼使用 setTimeout 包裹起來(lái)
function resolve(value) {
// 對(duì)于 resolve 函數(shù)來(lái)說(shuō)黄痪,首先需要判斷傳入的值是否為 Promise 類(lèi)型
if (value instanceof myPromise) {
return value.then(resolve, reject)
}
setTimeout(() => {
if (_this.state = 'pending') {
_this.value = value;
_this.onFullfiledCallBack.forEach(fn => fn(value));
_this.state = 'resolved';
}
}, 0)
}
function reject(reason) {
setTimeout(() => {
if (_this.state = 'pending') {
_this.reason = reason;
_this.onRejectCallBack.forEach(fn => fn(value));
_this.state = 'rejected';
}
}, 0)
}
接下來(lái)改造的then代碼:
myPromise.prototype.then = function (onFullfiled, onRejected) {
let self = this;
let promise2 = null;
onFullfiled = typeof onFullfiled === 'function' ? onFullfiled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r };
if (this.state == 'pending') {
return (promise2 = new myPromise((resolve, reject) => {
self.onFullfiledCallBack.push(() => {
try {
let x = onFullfiled(self.value);
//處理then不同的返回值和下面?zhèn)鬟f鏈的傳遞方式
resolutionProduce(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
});
self.onRejectCallBack.push(() => {
try {
let x = onRejected(self.value);
resolutionProduce(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
}))
}
}
思路跟之前基本一致紧帕,只是說(shuō)把之前返回值改成promise,同時(shí)捕獲異常,在status狀態(tài)為FULFILLED或者REJECTED的時(shí)候執(zhí)行得加上異步setTimeout包裹。 接下來(lái)完成最核心的resolutionProduce函數(shù):
function resolutionProduce(promise, x, resolve, reject) {
if (promise === x) {
reject(new TypeError('error'))
}
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 可能是個(gè)對(duì)象或者函數(shù)
try {
let then = x.then;
if (typeof then === 'function') {
//取出的then是函數(shù)是嗜,那么執(zhí)行函數(shù) 愈案,就將x作為函數(shù)的作用域 this 調(diào)用之,并且傳遞兩個(gè)回調(diào)函數(shù)作為參數(shù)鹅搪,第一個(gè)參數(shù)叫做 resolvePromise 站绪,第二個(gè)參數(shù)叫做 rejectPromise
let y = then.call(x, y => {
//遞歸調(diào)用
resolutionProduce(promise, y, resolve, reject);
}, r => reject(r))
} else {
resolve(x);
}
} catch (error) {
reject(error)
}
} else {
//可能是普通值
resolve(x); //把第一個(gè)then的返回值傳遞給返回的promise值里面的then函數(shù)
}
}
然后判斷 x 是否為對(duì)象或者函數(shù),如果都不是的話(huà)丽柿,將 x 傳入 resolve 中
如果 x 是對(duì)象或者函數(shù)的話(huà)恢准,先把 x.then 賦值給 then,然后判斷 then 的類(lèi)型甫题,如果不是函數(shù)類(lèi)型的話(huà)馁筐,就將 x 傳入 resolve 中
如果 then 是函數(shù)類(lèi)型的話(huà),就將 x 作為函數(shù)的作用域 this 調(diào)用之坠非,并且傳遞兩個(gè)回調(diào)函數(shù)作為參數(shù)敏沉,第一個(gè)參數(shù)叫做 resolvePromise ,第二個(gè)參數(shù)叫做 rejectPromise炎码,如果Promise對(duì)象轉(zhuǎn)為成功態(tài)或是失敗時(shí)傳入的還是一個(gè)Promise對(duì)象盟迟,此時(shí)應(yīng)該繼續(xù)執(zhí)行,直到最后的Promise執(zhí)行完潦闲。所以遞歸操作(這一點(diǎn)我有點(diǎn)繞)
以上代碼在執(zhí)行的過(guò)程中如果拋錯(cuò)了攒菠,將錯(cuò)誤傳入 reject 函數(shù)中
最后極簡(jiǎn)版手寫(xiě)promise就完成了
最后使用diamante驗(yàn)證:
let p = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve('1')
}, 1000)
})
p.then(data =>{
console.log(data)
return new myPromise((resolve,reject)=>{
setTimeout(()=>{
console.log('我是then里面打印出來(lái)的');
resolve('2222')
},1000)
})
} ).then(data=>console.log(data));
能正確打印 結(jié)束!
最終完整代碼:
function myPromise(executor) {
let _this = this;
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFullfiledCallBack = [];
this.onRejectCallBack = [];
function resolve(value) {
// 對(duì)于 resolve 函數(shù)來(lái)說(shuō)矫钓,首先需要判斷傳入的值是否為 Promise 類(lèi)型
if (value instanceof myPromise) {
return value.then(resolve, reject)
}
setTimeout(() => {
if (_this.state = 'pending') {
_this.value = value;
_this.onFullfiledCallBack.forEach(fn => fn(value));
_this.state = 'resolved';
}
}, 0)
}
function reject(reason) {
setTimeout(() => {
if (_this.state = 'pending') {
_this.reason = reason;
_this.onRejectCallBack.forEach(fn => fn(value));
_this.state = 'rejected';
}
}, 0)
}
try {
executor(resolve, reject); //馬上執(zhí)行
} catch (error) {
reject(error)
}
}
myPromise.prototype.then = function (onFullfiled, onRejected) {
let self = this;
let promise2 = null;
onFullfiled = typeof onFullfiled === 'function' ? onFullfiled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r };
if (this.state == 'pending') {
return (promise2 = new myPromise((resolve, reject) => {
self.onFullfiledCallBack.push(() => {
try {
let x = onFullfiled(self.value);
//處理then不同的返回值和下面?zhèn)鬟f鏈的傳遞方式
resolutionProduce(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
});
self.onRejectCallBack.push(() => {
try {
let x = onRejected(self.value);
resolutionProduce(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
}))
}
}
//處理then返回值如何傳遞和下一個(gè)then的值
function resolutionProduce(promise, x, resolve, reject) {
if (promise === x) {
reject(new TypeError('error'))
}
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 可能是個(gè)對(duì)象或者函數(shù)
try {
let then = x.then;
if (typeof then === 'function') {
//取出的then是函數(shù)要尔,那么執(zhí)行函數(shù) ,就將x作為函數(shù)的作用域 this 調(diào)用之新娜,并且傳遞兩個(gè)回調(diào)函數(shù)作為參數(shù)赵辕,第一個(gè)參數(shù)叫做 resolvePromise ,第二個(gè)參數(shù)叫做 rejectPromise
let y = then.call(x, y => {
//遞歸調(diào)用
resolutionProduce(promise, y, resolve, reject);
}, r => reject(r))
} else {
resolve(x);
}
} catch (error) {
reject(error)
}
} else {
//可能是普通值
resolve(x); //把第一個(gè)then的返回值傳遞給返回的promise值里面的then函數(shù)
}
}
let p = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve('1')
}, 1000)
})
p.then(data =>{
console.log(data)
return new myPromise((resolve,reject)=>{
setTimeout(()=>{
console.log('我是then里面打印出來(lái)的');
resolve('2222')
},1000)
})
} ).then(data=>console.log(data));
主要還是參考了幾篇文章:
http://www.reibang.com/p/c633a22f9e8c
https://blog.csdn.net/weixin_34348111/article/details/91374448
https://segmentfault.com/a/1190000020505870