我們先定義一個Promise 類。
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
let resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECT;
this.reason = reason;
}
}
try {
executor(resolve, reject)
//直接執(zhí)行excutor并傳入resolve和reject方法。
} catch (err) {
reject(err)
}
}
}
我們來分析一下竿屹,首先executor(執(zhí)行器)是什么呢恒水?
let promise = new Promise((res,rej) => {
res('成功了')姿现;
})
那么這個時候executor就是:
(res,rej) => {
res('成功了');
}
之后在這個構造函數中又定義了status(狀態(tài)默認PENDING)、成功狀態(tài)數據:value(默認undefined)、失敗狀態(tài)的原因:reason(默認undefined)速警、以及resolve、reject方法鸯两,最后通過try直接執(zhí)行闷旧。
可以看到給excutor傳入了resolve,reject方法钧唐。
那么上面寫的:
(res,rej) => {
res('成功了')忙灼;
}
中的res,rej就是定義在構造函數上的方法。執(zhí)行res('成功了')该园,相當于執(zhí)行:
let resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
}
并傳入‘成功了’為value的實參酸舍。
我們再加上then方法,
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
}
if (this.status === REJECTED) {
onRejected(this.reason)
}
}
}
注意:這里的then不是constructor里面的里初,而是原型上的父腕。
不過這樣寫有一個問題。
let promise = new Promise((res, rej) => {
setTimeout(() => {
res('成功了')
}, 1000);
}).then(res => {
console.log(res)
})//ReferenceError: PENDING is not defined
這樣寫會報錯青瀑,這是為什么呢?
因為setTimeout是異步操作萧诫,所以會先執(zhí)行then斥难,而這個時候由于還沒執(zhí)行resolve或reject方法,status仍為PENDING狀態(tài)帘饶,所以會報錯哑诊。
那我們改一下代碼。
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach(fn => fn());
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECT;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject)
//直接執(zhí)行excutor并傳入resolve和reject方法及刻。
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
}
if (this.status === REJECTED) {
onRejected(this.reason)
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
onFulfilled(this.value)
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason);
})
}
}
}
then方法接受兩個參數镀裤,我們給then方法執(zhí)行的的這兩個參數(也是方法)傳入this.value以及this.reason。并將這兩個方法分別保存onResolvedCallbacks和onRejectedCallbacks中缴饭。但定時器進入回調隊列并且主線程任務執(zhí)行完以后暑劝,調用resolve或reject方法,
let resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach(fn => fn());
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECT;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
可以看出來在調用onResolvedCallbacks或onRejectedCallbacks函數以前颗搂,就已經賦值了this.value和this.reason担猛。
let promise = new Promise((res, rej) => {
setTimeout(() => {
res('成功了')
}, 1000);
}).then(res => {
console.log(res)
})//成功了
1秒后打印成功了。
哎呀丢氢,那這里為啥子要用forEach遍歷整個數組呢傅联?因為,要知道疚察,Promise使用then方法后會返回一個Promise對象蒸走。而這樣,就說明一個Promise可以調用多次then方法貌嫡,那每個then方法中如果都使用到異步比驻,那么forEach就顯得很有用了。后面再舉個例子哈衅枫。
我們知道then方法是可以不傳參數的嫁艇,那我們怎么進行數據的傳遞呢?
另外弦撩,
let promise = new Promise((res,rej) => {
res(10);
});
let test = promise.then(res => {
return test//TypeError: Chaining cycle detected for promise #<Promise>
})
這種當then的返回值與本身相同的時候步咪,怎么解決?
先改一下then方法益楼。
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v=>v;
onRejected = typeof onRejected === 'function' ? onRejected : err => {
throw err
};//通過這兩行代碼解決then方法不傳入參數的問題猾漫。
let promise2 = new Promise((res, rej) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, res, rej)
} catch (e) {
reject(e)
}
}, 0);
}
if (this.status === REJECT) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, res, rej)
} catch (e) {
reject(e)
}
}, 0);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, res, rej)
} catch (e) {
reject(e)
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.value);
resolvePromise(promise2, x, res, rej)
} catch (e) {
reject(e)
}
}, 0);
})
}
});
return promise2
}
}
resolvePromise(promise2, x, res, rej)方法用于解決單then的返回值與本身相同的問題点晴,以及其他返回值問題例如,返回簡單數據類型悯周,function粒督,其他的promise,而其他的promise中又有promise等問題禽翼。
前面也說了定時器是異步的屠橄,三個if中都借用異步,為的是先返回promise2闰挡,在執(zhí)行定時器里的代碼锐墙,不這么用,promise2都還沒執(zhí)行完呢长酗。
那么這樣就簡單了溪北,promise2是then的返回值,x可以使任意值夺脾。
再來定義resolvePromise
//這是定義在全局作用域中的
const resolvePromise = (promise2, x, resolve, reject) => {
// 自己等待自己完成是錯誤的實現(xiàn)之拨,用一個類型錯誤,結束掉
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
let called;
// 后續(xù)的條件要嚴格判斷 保證代碼能和別的庫一起使用
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, y => { // 根據 promise 的狀態(tài)決定是成功還是失敗
if (called) return;
called = true;
// 遞歸解析的過程(因為可能 promise 中還有 promise) Promise/A+ 2.3.3.3.1
resolvePromise(promise2, y, resolve, reject);
}, r => {
// 只要失敗就失敗 Promise/A+ 2.3.3.3.2
if (called) return;
called = true;
reject(r);
});
} else {
// 如果 x.then 是個普通值就直接返回 resolve 作為結果 Promise/A+ 2.3.3.4
resolve(x);
}
} catch (e) {
// Promise/A+ 2.3.3.2
if (called) return;
called = true;
reject(e)
}
} else {
// 如果 x 是個普通值就直接返回 resolve 作為結果 Promise/A+ 2.3.4
resolve(x)
}
}
一步一步來咧叭,
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
這一步就是解決then return 自己的時候的問題蚀乔。
后面的代碼就有長了,首先判斷是不是復雜(引用數據類型)菲茬,
if ((typeof x === 'object' && x != null) || typeof x === 'function')
不是的話就說明是簡單數據類型乙墙,直接就:
else {
// 如果 x 是個普通值就直接返回 resolve 作為結果
resolve(x)
}
再判斷這個復雜類型有沒有then方法
if (typeof then === 'function')
結果為true的話就說明這個x是Promise對象,否則就是簡單數據類型生均,直接resolve
else {
// 如果 x.then 是個普通值就直接返回 resolve 作為結果
resolve(x);
}
而最中間的代碼听想,
then.call(x, y => { // 根據 promise 的狀態(tài)決定是成功還是失敗
if (called) return;
called = true;
// 遞歸解析的過程(因為可能 promise 中還有 promise) Promise/A+ 2.3.3.3.1
resolvePromise(promise2, y, resolve, reject);
}, r => {
// 只要失敗就失敗 Promise/A+ 2.3.3.3.2
if (called) return;
called = true;
reject(r);
});
就像注釋的那樣。
而之前說的為什么要遍歷onResolvedCallbacks或onRejectedCallbacks是因為马胧,我可能異步操作promise中的reject或resolve汉买,并在后面調用then方法,而在這個then方法中return出來一個新的Promise佩脊,再次借助定時器使得這個promise狀態(tài)也為PENDING以此類推蛙粘。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
reject('失敗');
}, 1000)
}).then(null, err => {
return new Promise((res, rej) => {
setTimeout(() => {
console.log(err);
rej('失敗1');
}, 2000)
})
}).then(null, err => {
return new Promise((res, rej) => {
setTimeout(() => {
console.log(err);
rej('失敗2');
}, 3000)
})
}).then(data => {
console.log(data);
}, err => {
console.log('err', err);
})
而現(xiàn)在的整個代碼為:
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
const resolvePromise = (promise2, x, resolve, reject) => {
// 自己等待自己完成是錯誤的實現(xiàn),用一個類型錯誤威彰,結束掉 promise Promise/A+ 2.3.1
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// Promise/A+ 2.3.3.3.3 只能調用一次
let called;
// 后續(xù)的條件要嚴格判斷 保證代碼能和別的庫一起使用
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
try {
// 為了判斷 resolve 過的就不用再 reject 了(比如 reject 和 resolve 同時調用的時候) Promise/A+ 2.3.3.1
let then = x.then;
if (typeof then === 'function') {
// 不要寫成 x.then出牧,直接 then.call 就可以了 因為 x.then 會再次取值,Object.defineProperty Promise/A+ 2.3.3.3
then.call(x, y => { // 根據 promise 的狀態(tài)決定是成功還是失敗
if (called) return;
called = true;
// 遞歸解析的過程(因為可能 promise 中還有 promise) Promise/A+ 2.3.3.3.1
resolvePromise(promise2, y, resolve, reject);
}, r => {
// 只要失敗就失敗 Promise/A+ 2.3.3.3.2
if (called) return;
called = true;
reject(r);
});
} else {
// 如果 x.then 是個普通值就直接返回 resolve 作為結果 Promise/A+ 2.3.3.4
resolve(x);
}
} catch (e) {
// Promise/A+ 2.3.3.2
if (called) return;
called = true;
reject(e)
}
} else {
// 如果 x 是個普通值就直接返回 resolve 作為結果 Promise/A+ 2.3.4
resolve(x)
}
}
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach(fn => fn());
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
//解決 onFufilled歇盼,onRejected 沒有傳值的問題
//Promise/A+ 2.2.1 / Promise/A+ 2.2.5 / Promise/A+ 2.2.7.3 / Promise/A+ 2.2.7.4
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
//因為錯誤的值要讓后面訪問到舔痕,所以這里也要跑出個錯誤,不然會在之后 then 的 resolve 中捕獲
onRejected = typeof onRejected === 'function' ? onRejected : err => {
throw err
};
// 每次調用 then 都返回一個新的 promise Promise/A+ 2.2.7
let promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
//Promise/A+ 2.2.2
//Promise/A+ 2.2.4 --- setTimeout
setTimeout(() => {
try {
//Promise/A+ 2.2.7.1
let x = onFulfilled(this.value);
// x可能是一個proimise
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
//Promise/A+ 2.2.7.2
reject(e)
}
}, 0);
}
if (this.status === REJECTED) {
//Promise/A+ 2.2.3
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
}, 0);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0);
});
}
});
return promise2;
}
}