參考文章:https://juejin.cn/post/6850037281206566919#heading-4
基礎(chǔ)版 Promise
const p1 = new Promise((resolve, reject) => {
console.log('create a promise');
resolve('成功了');
})
console.log("after new promise");
const p2 = p1.then(data => {
console.log(data)
throw new Error('失敗了')
})
const p3 = p2.then(data => {
console.log('success', data)
}, err => {
console.log('faild', err)
})
// 三個(gè)狀態(tài):PENDING抚官、FULFILLED李滴、REJECTED
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
// 默認(rèn)狀態(tài)為 PENDING
this.status = PENDING;
// 存放成功狀態(tài)的值,默認(rèn)為 undefined
this.value = undefined;
// 存放失敗狀態(tài)的值,默認(rèn)為 undefined
this.reason = undefined;
// 調(diào)用此方法就是成功
let resolve = (value) => {
// 狀態(tài)為 PENDING 時(shí)才可以更新狀態(tài)致燥,防止 executor 中調(diào)用了兩次 resovle/reject 方法
if(this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
}
// 調(diào)用此方法就是失敗
let reject = (reason) => {
// 狀態(tài)為 PENDING 時(shí)才可以更新狀態(tài)蹭沛,防止 executor 中調(diào)用了兩次 resovle/reject 方法
if(this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
}
}
try {
// 立即執(zhí)行,將 resolve 和 reject 函數(shù)傳給使用者
executor(resolve,reject)
} catch (error) {
// 發(fā)生異常時(shí)執(zhí)行失敗邏輯
reject(error)
}
}
// 包含一個(gè) then 方法送淆,并接收兩個(gè)參數(shù) onFulfilled税产、onRejected
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
}
if (this.status === REJECTED) {
onRejected(this.reason)
}
}
}
寫完代碼我們可以測試一下:
const promise = new Promise((resolve, reject) => {
resolve('成功');
}).then(
(data) => {
console.log('success', data)
},
(err) => {
console.log('faild', err)
}
)
控制臺輸出:
"success"
"成功"
異步操作
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
// 存放成功的回調(diào)
this.onResolvedCallbacks = [];
// 存放失敗的回調(diào)
this.onRejectedCallbacks= [];
let resolve = (value) => {
if(this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// 依次將對應(yīng)的函數(shù)執(zhí)行
this.onResolvedCallbacks.forEach(fn=>fn());
}
}
let reject = (reason) => {
if(this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 依次將對應(yīng)的函數(shù)執(zhí)行
this.onRejectedCallbacks.forEach(fn=>fn());
}
}
try {
executor(resolve,reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
}
if (this.status === REJECTED) {
onRejected(this.reason)
}
if (this.status === PENDING) {
// 如果promise的狀態(tài)是 pending,需要將 onFulfilled 和 onRejected 函數(shù)存放起來偷崩,等待狀態(tài)確定后辟拷,再依次將對應(yīng)的函數(shù)執(zhí)行
this.onResolvedCallbacks.push(() => {
onFulfilled(this.value)
});
// 如果promise的狀態(tài)是 pending,需要將 onFulfilled 和 onRejected 函數(shù)存放起來阐斜,等待狀態(tài)確定后梧兼,再依次將對應(yīng)的函數(shù)執(zhí)行
this.onRejectedCallbacks.push(()=> {
onRejected(this.reason);
})
}
}
}
測試一下:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功');
},1000);
}).then(
(data) => {
console.log('success', data)
},
(err) => {
console.log('faild', err)
}
)
控制臺等待 1s 后輸出:
"success 成功"
ok!大功告成智听,異步問題已經(jīng)解決了羽杰!
熟悉設(shè)計(jì)模式的同學(xué),應(yīng)該意識到了這其實(shí)是一個(gè)發(fā)布訂閱模式到推,這種收集依賴 -> 觸發(fā)通知 -> 取出依賴執(zhí)行的方式考赛,被廣泛運(yùn)用于發(fā)布訂閱模式的實(shí)現(xiàn)。
then 的鏈?zhǔn)秸{(diào)用&值穿透特性
我們都知道莉测,promise 的優(yōu)勢在于可以鏈?zhǔn)秸{(diào)用颜骤。在我們使用 Promise 的時(shí)候,當(dāng) then 函數(shù)中 return 了一個(gè)值捣卤,不管是什么值忍抽,我們都能在下一個(gè) then 中獲取到,這就是所謂的then 的鏈?zhǔn)秸{(diào)用董朝。而且鸠项,當(dāng)我們不在 then 中放入?yún)?shù),例:promise.then().then()子姜,那么其后面的 then 依舊可以得到之前 then 返回的值祟绊,這就是所謂的值的穿透。那具體如何實(shí)現(xiàn)呢哥捕?簡單思考一下牧抽,如果每次調(diào)用 then 的時(shí)候,我們都重新創(chuàng)建一個(gè) promise 對象遥赚,并把上一個(gè) then 的返回結(jié)果傳給這個(gè)新的 promise 的 then 方法扬舒,不就可以一直 then 下去了么?那我們來試著實(shí)現(xiàn)一下凫佛。
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
const resolvePromise = (promise2, x, resolve, reject) => {
// 自己等待自己完成是錯(cuò)誤的實(shí)現(xiàn)讲坎,用一個(gè)類型錯(cuò)誤泽腮,結(jié)束掉 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 只能調(diào)用一次
let called;
// 后續(xù)的條件要嚴(yán)格判斷 保證代碼能和別的庫一起使用
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
try {
// 為了判斷 resolve 過的就不用再 reject 了(比如 reject 和 resolve 同時(shí)調(diào)用的時(shí)候) Promise/A+ 2.3.3.1
let then = x.then;
if (typeof then === 'function') {
// 不要寫成 x.then,直接 then.call 就可以了 因?yàn)?x.then 會再次取值衣赶,Object.defineProperty Promise/A+ 2.3.3.3
then.call(x, y => { // 根據(jù) promise 的狀態(tài)決定是成功還是失敗
if (called) return;
called = true;
// 遞歸解析的過程(因?yàn)榭赡?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 是個(gè)普通值就直接返回 resolve 作為結(jié)果 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 是個(gè)普通值就直接返回 resolve 作為結(jié)果 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;
//因?yàn)殄e(cuò)誤的值要讓后面訪問到,所以這里也要跑出個(gè)錯(cuò)誤府瞄,不然會在之后 then 的 resolve 中捕獲
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
// 每次調(diào)用 then 都返回一個(gè)新的 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可能是一個(gè)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;
}
}
測試一下:
const promise = new Promise((resolve, reject) => {
reject('失敗');
}).then().then().then(data=>{
console.log(data);
},err=>{
console.log('err',err);
})
控制臺輸出:
"失敗 err"
至此碧磅,我們已經(jīng)完成了 promise 最關(guān)鍵的部分:then 的鏈?zhǔn)秸{(diào)用和值的穿透。搞清楚了 then 的鏈?zhǔn)秸{(diào)用和值的穿透遵馆,你也就搞清楚了 Promise鲸郊。