聊聊 Promise 你不会注意的点

1,036 阅读3分钟

前言

前一篇文章梳理了promise的基础聊聊promise系列(基础)

这篇文章将从容易出现错误的几种用法出发,得出如何去正确的用promise。

如何避免promise的回调地狱

想必大家在项目中会见过这样的写法。

const p1 = new promise((resolve, reject) => {
    resolve(true);
})
p1.then((res) => {
    if(res) {
        const p2 = new promise((resolve, reject) => {
            resolve(true);
        })
        p2.then((res) => {
            const p3 = new promise((resolve, reject) => {
                resolve(true);
            })
        })
    }
})

这个写法写出了回调地狱的感觉。那如何优化他呢!!

const p1 = new promise((resolve, reject) => {
    resolve(true);
})
const p2 = new promise((resolve, reject) => {
    resolve(true);
})
const p3 = new promise((resolve, reject) => {
    resolve(true);
})
p1.then((res) => {
    if(res) {
        return p2
    }
}).then((res) => {
    if(res) {
        return p3
    }
})

用这种链式调用的方式还是十分的优雅的。

catch() 与 then(null, ...) 并非完全等价

举个等价的例子:

Promise.reject('error').catch((res) => {
    console.log(res);
})
Promise.reject('error').then(null, (res) => {
    console.log(res);
})

catch是用then(null, ...)来实现的,但是在某些条件下,并不是等价的,比如以下例子:

const p1 = new Promise((resolve, reject) => {
    if(/*正确的条件*/) {
        resolve('success');
    } else {
        reject('false');
    }
});
//用法一:
p1.then((res) => {
    console.log(res);
    throw new Error('我在then出错了');
}, (res) => {
    console.log(res);
});
//用法二:
p1.then((res) => {
    console.log(res);
    throw new Error('我在then出错了');
}).catch((res) => {
    console.log(res);
})
上述例子的区别就在与,catch可以捕捉then中发生的错误,但是then的第二个回调函数捕捉不了第一个参数中的报错。

输出的结果:

避免promise穿透问题

什么是promise穿透问题呢?举个例子:

Promise.resolve('foo').then(Promise.resolve('bar')).then(function (result) {
  console.log(result);
});

这个鬼东西会输出啥??

斟酌良久,你可能回答bar,那你就错了,实际输出的是foo。

因为then中希望获得的是一个函数。而当你传入一个promise的时候,相当于then(null),无论你添加多少个then(null),promise都会穿透到下一个。

当然,对于上述例子,你可能希望的是这样做:

Promise.resolve('foo').then(() => {
    return Promise.resolve('bar');
}).then(function (result) {
  console.log(result);
});

切记,一定要往then里面传入函数

几种不同的写法带来的后果

接下来,我们通过一个例子,来看一下不同写法带来的后果。

const func = function(last, num) {
  return new Promise((resolve, reject)=>{
      console.log(last, num);
      resolve(num);
  });
};

// 写法一
Promise.resolve()
  .then((last)=>{return func(last, 1);})
  .then((last)=>{return func(last, 2);})
  .then((last)=>{return func(last, 3);});

// 写法二
Promise.resolve()
  .then((last)=>{func(last, 1);})
  .then((last)=>{func(last, 2);})
  .then((last)=>{func(last, 3);});
  
 // 写法三
Promise.resolve()
  .then(func(undefined, 1))
  .then(func(undefined, 2))
  .then(func(undefined, 3));
  
 // 写法四
func(undefined, 1).then(func(undefined, 2)).then(func(undefined, 3));
  • 写法一

结果:

undefined 1
1 2
2 3

这是我们想要的结果,也是promise链式调用正确的写法。

  • 写法二 结果:
undefined 1
undefined 2
undefined 3

虽然在then中执行了函数,但是并没有return有结果的promise给下一个then,所以后面的then永远是收不到结果值的。

  • 写法三

结果:

undefined 1
undefined 2
undefined 3

这种写法是造成promise穿透引起的。then里面需要是一个函数,但是却传了一个promise进去。

  • 写法四

结果:

undefined 1
undefined 2
undefined 3

和第三种也发一致,也是promise穿透,then里面需要是一个函数,但是却传了一个promise进去。