深刻理解Promise系列(三):从几个问题再次探讨Promise

问题一

系列(一)的开头,我曾经举过一个例子,但其中包含着错误(现已更正),现简化如下:

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】
});

原因
之所以会出现这种情况,是因为 promise 对象的每一个 then 其实是返回一个全新的 promise 对象,而当没有显式的指定返回的 promise
对象时,它会返回一个以 undefined 值 resolve 的 promise 对象,所以有如下代码:

let defaultP = p1.then(res => {
  console.log(res); // 此处输出为1
  // 此处返回默认的promise对象,它会立即以undefined值resolve
});
defaultP.then(res => {
  console.log(res); // 此处输出为undefined
});

因此,p2接收到的 value 其实是 undefined,所以 2 + value 变成了NaN

使用Promise.resolve()

那么要想达到预期的结果,就要我们主动的返回一个 promise 对象,我们想让它以 p1 的 fulfilled 值来 resolve,这时使用 Promise 的 resolve 方法可以很简洁的实现:

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不仅可以接受一个值作为参数,还可以接受
promise 对象或者 thenable 对象作为参数:

// 传入一个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!
});

详细请参考 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
});

这是我在研究“问题一”时无意在网上发现的另一个问题,其实并不难只是对初学者来说比较绕,所以拿来解析一下,其原理和“问题一”也很相似:
为什么第一个 catch 没有输出呢?很简单,因为 core.then(resolve, reject) 返回的是之前说的默认的 promise 对象,这个 promise 对象是以 undefined 值 resolve 的,因为没有 reject 所以无法被 catch。
因此要想达到预期效果有两种方式:

// 方式一,证明我们的理论
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 之所以可以达到预期效果,是因为 Promise.resolve() 方法传入的 promise 或 thenable 对象 reject 之后,它返回的 promise 对象也会 reject,进而触发 catch:

let p = Promise.resolve({
  then: (resolve, reject) => {
    return core.then(resolve, reject)
    // 【核心】,此处是将then对象的reject传入到core中,作为core reject的回调
    // 所以当core reject的时候,then对象也会reject
    .catch(err => {
      console.log('1:', err);
    });
  }
}).catch(err => {
  console.log('2:', err);
});

总结

当我们理解了Promise内部的实现原理,一切绕来绕去的怪异问题都是纸老虎 O(∩_∩)O~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容