問題一
在系列(一)的開頭,我曾經(jīng)舉過一個例子羽利,但其中包含著錯誤(現(xiàn)已更正)宫患,現(xiàn)簡化如下:
let p1 = new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1);
}, 1000);
});
function p2(value) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2 + value);
}, 1000);
});
}
p1.then(res => {
console.log(res); // 1000ms后輸出1,【這里是正確的】
}).then(p2).then(res => {
console.log(res); // 再過1000ms后輸出3这弧,【這里是錯誤的娃闲,輸出NaN】
});
原因:
之所以會出現(xiàn)這種情況,是因為 promise 對象的每一個 then 其實是返回一個全新的 promise 對象匾浪,而當(dāng)沒有顯式的指定返回的 promise
對象時皇帮,它會返回一個以 undefined 值 resolve 的 promise 對象,所以有如下代碼:
let defaultP = p1.then(res => {
console.log(res); // 此處輸出為1
// 此處返回默認(rèn)的promise對象蛋辈,它會立即以undefined值resolve
});
defaultP.then(res => {
console.log(res); // 此處輸出為undefined
});
因此属拾,p2接收到的 value 其實是 undefined,所以 2 + value 變成了NaN
使用Promise.resolve()
那么要想達(dá)到預(yù)期的結(jié)果冷溶,就要我們主動的返回一個 promise 對象渐白,我們想讓它以 p1 的 fulfilled 值來 resolve,這時使用 Promise 的 resolve 方法可以很簡潔的實現(xiàn):
let notDefaultP = p1.then(res => {
console.log(res); // 此處輸出為1
return Promise.resolve(res); // 此處返回一個以res值resolve的promise對象
});
notDefaultP.then(res => {
console.log(res); // 此處輸出為1
});
Promise.resolve不僅可以接受一個值作為參數(shù)逞频,還可以接受
promise 對象或者 thenable 對象作為參數(shù):
// 傳入一個promise對象
let p = new Promise(resolve => {
resolve("I'm a promise!");
});
let p2 = Promise.resolve(p);
p2.then(res => {
console.log(res); // I'm a promise!
});
console.log(p2 === p); // true
// 傳入一個thenable對象
let thenable = {
then: fulfilled => fulfilled("I'm thenable!")
};
let p3 = Promise.resolve(thenable);
p3.then(res => {
console.log(res); // I'm thenable!
});
詳細(xì)請參考 MDN
問題二
let core = new Promise((resolve, reject) => {
reject(1);
});
let p = Promise.resolve({
then: (resolve, reject) => {
return core.then(resolve, reject).catch(err => {
console.log('1:', err); // 此處無輸出
});
}
}).catch(err => {
console.log('2:', err); // 此處輸出 2:1
});
這是我在研究“問題一”時無意在網(wǎng)上發(fā)現(xiàn)的另一個問題纯衍,其實并不難只是對初學(xué)者來說比較繞,所以拿來解析一下苗胀,其原理和“問題一”也很相似:
為什么第一個 catch 沒有輸出呢托酸?很簡單,因為 core.then(resolve, reject) 返回的是之前說的默認(rèn)的 promise 對象柒巫,這個 promise 對象是以 undefined 值 resolve 的励堡,因為沒有 reject 所以無法被 catch。
因此要想達(dá)到預(yù)期效果有兩種方式:
// 方式一堡掏,證明我們的理論
return core.then(resolve, reject).then(res => {
console.log('1:', res); // 此處會輸出 1:undefined
});
// 方式二
return core.then(resolve, reason => {
reject(reason);
return Promise.reject(reason); // 主動返回一個以reason值reject的promise對象
}).catch(err => {
console.log('1:', err); // 此處會輸出 1:1
});
而第二個 catch 之所以可以達(dá)到預(yù)期效果应结,是因為 Promise.resolve() 方法傳入的 promise 或 thenable 對象 reject 之后,它返回的 promise 對象也會 reject,進(jìn)而觸發(fā) catch:
let p = Promise.resolve({
then: (resolve, reject) => {
return core.then(resolve, reject)
// 【核心】鹅龄,此處是將then對象的reject傳入到core中揩慕,作為core reject的回調(diào)
// 所以當(dāng)core reject的時候,then對象也會reject
.catch(err => {
console.log('1:', err);
});
}
}).catch(err => {
console.log('2:', err);
});
總結(jié)
當(dāng)我們理解了Promise內(nèi)部的實現(xiàn)原理扮休,一切繞來繞去的怪異問題都是紙老虎 O(∩_∩)O~