深刻理解Promise系列(三):從幾個問題再次探討Promise

問題一

系列(一)的開頭,我曾經(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~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容