問題一
在系列(一)的開頭,我曾經(jīng)舉過一個(gè)例子,但其中包含著錯(cuò)誤(現(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,【這里是錯(cuò)誤的,輸出NaN】
});
原因:
之所以會(huì)出現(xiàn)這種情況,是因?yàn)?promise 對(duì)象的每一個(gè) then 其實(shí)是返回一個(gè)全新的 promise 對(duì)象,而當(dāng)沒有顯式的指定返回的 promise
對(duì)象時(shí),它會(huì)返回一個(gè)以 undefined 值 resolve 的 promise 對(duì)象,所以有如下代碼:
let defaultP = p1.then(res => {
console.log(res); // 此處輸出為1
// 此處返回默認(rèn)的promise對(duì)象,它會(huì)立即以u(píng)ndefined值resolve
});
defaultP.then(res => {
console.log(res); // 此處輸出為undefined
});
因此,p2接收到的 value 其實(shí)是 undefined,所以 2 + value 變成了NaN
使用Promise.resolve()
那么要想達(dá)到預(yù)期的結(jié)果,就要我們主動(dòng)的返回一個(gè) promise 對(duì)象,我們想讓它以 p1 的 fulfilled 值來 resolve,這時(shí)使用 Promise 的 resolve 方法可以很簡潔的實(shí)現(xiàn):
let notDefaultP = p1.then(res => {
console.log(res); // 此處輸出為1
return Promise.resolve(res); // 此處返回一個(gè)以res值resolve的promise對(duì)象
});
notDefaultP.then(res => {
console.log(res); // 此處輸出為1
});
Promise.resolve不僅可以接受一個(gè)值作為參數(shù),還可以接受
promise 對(duì)象或者 thenable 對(duì)象作為參數(shù):
// 傳入一個(gè)promise對(duì)象
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
// 傳入一個(gè)thenable對(duì)象
let thenable = {
then: fulfilled => fulfilled("I'm thenable!")
};
let p3 = Promise.resolve(thenable);
p3.then(res => {
console.log(res); // I'm thenable!
});
詳細(xì)請(qǐng)參考 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
});
這是我在研究“問題一”時(shí)無意在網(wǎng)上發(fā)現(xiàn)的另一個(gè)問題,其實(shí)并不難只是對(duì)初學(xué)者來說比較繞,所以拿來解析一下,其原理和“問題一”也很相似:
為什么第一個(gè) catch 沒有輸出呢?很簡單,因?yàn)?core.then(resolve, reject) 返回的是之前說的默認(rèn)的 promise 對(duì)象,這個(gè) promise 對(duì)象是以 undefined 值 resolve 的,因?yàn)闆]有 reject 所以無法被 catch。
因此要想達(dá)到預(yù)期效果有兩種方式:
// 方式一,證明我們的理論
return core.then(resolve, reject).then(res => {
console.log('1:', res); // 此處會(huì)輸出 1:undefined
});
// 方式二
return core.then(resolve, reason => {
reject(reason);
return Promise.reject(reason); // 主動(dòng)返回一個(gè)以reason值reject的promise對(duì)象
}).catch(err => {
console.log('1:', err); // 此處會(huì)輸出 1:1
});
而第二個(gè) catch 之所以可以達(dá)到預(yù)期效果,是因?yàn)?Promise.resolve() 方法傳入的 promise 或 thenable 對(duì)象 reject 之后,它返回的 promise 對(duì)象也會(huì) reject,進(jìn)而觸發(fā) catch:
let p = Promise.resolve({
then: (resolve, reject) => {
return core.then(resolve, reject)
// 【核心】,此處是將then對(duì)象的reject傳入到core中,作為core reject的回調(diào)
// 所以當(dāng)core reject的時(shí)候,then對(duì)象也會(huì)reject
.catch(err => {
console.log('1:', err);
});
}
}).catch(err => {
console.log('2:', err);
});
總結(jié)
當(dāng)我們理解了Promise內(nèi)部的實(shí)現(xiàn)原理,一切繞來繞去的怪異問題都是紙老虎 O(∩_∩)O~