欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

javascript异步之Promise then和catch

程序员文章站 2022-05-08 16:09:50
...

javascript异步之Promise then和catch

这是javascript异步系列文章的第六篇
前面介绍了Promise.all()、Promise.race()、Promise.finally()、resolve()、reject()
今天讨论一下then和catch
我们展开promise的原型链,看看有哪些实例方法

const p1 = new Promise((resolve, reject) => {

})
console.log(p1.__proto__);

 

javascript异步之Promise then和catch
p1是Promise的一个实例,我们展开p1的原型链,可以看到
p1具有then方法,catch方法,还有finally方法(这个方法我们之前讨论过)
也就是说,这些方法是定义在原型对象Promise.prototype上的。

then方法

目录

它的作用是为 Promise 实例添加状态改变时的回调函数。then方法可以有两个参数(第二个可选),

  • 第一个是resolve状态的回调函数
  • 第二个是reject状态的回调函数(可选)

then方法返回的是一个新的Promise实例(不是原来那个Promise实例)我们前面说过Promise接收一个函数作为参数,这个函数有两个参数,第二个参数可选,分别是resolve和reject,这两个参数分别和then方法的两个参数对应。
then方法其实并不简单,我们看几个栗子
观察如下代码,想一想最后输出什么?

一个经典栗子

Promise.reject('出错啦')
    .then(res => console.log(`res1:${res}`), err => console.log(`err1:${err}`))
    .then(res => console.log(`res2:${res}`), err => console.log(`err2:${err}`));

JavaScript

Copy

不卖关子,直接告诉你答案:

//=>err1:出错啦
//=>res2:undefined

JavaScript

Copy

可能和你想的不一样,或者看着输出反推,也没找出个所以然,
我们一起分析一下原因
Promise.reject()方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
以下两种写法等价

const p1 = Promise.reject('出错啦')
//和下面的方法等效
const p2 = new Promise((resolve, reject) => {
    reject('出错啦')
})

JavaScript

Copy

resolve和reject当传入一个字符串作为参数时,则会返回一个新的Promise实例
如果是resolve,新的Promise对象的状态为resolve
如果是reject,新的Promise对象的状态为reject
上面的p1 和p2 都会返回一个新的Promise实例,并且状态为reject

const p1 = Promise.reject('出错啦')
p1.then(res => {
    console.log(`res1:${res}`)//“这里不执行”
}, err => {
    console.log(`err1:${err}`)//=>err1:出错啦
})
//最后输出err1:出错啦

JavaScript

Copy

因为p1返回的Promise实例状态为reject,所以执行了then下面第二个参数的方法
到这里可能会有一个疑问,then方法的第二个参数可选,在这里如果不用第二个参数会怎么样呢?

const p1 = Promise.reject('出错啦')
p1.then(res => {
    console.log(`res1:${res}`)
})
// .catch(err=>{
//   console.log(`err:${err}`)
// })
//=>Uncaught (in promise) 出错啦

JavaScript

Copy

会报一个未捕获的错,当省略第二个参数时,catch的存在就很有意义 ,关于catch我们稍后讨论
上面对第一个then的输出做了讨论,我们继续看第二then
在解释第二个then之前,我们先看另一个栗子

关于返回值

const p3 = new Promise((resolve, reject) => {
          reject('出错啦');
      })
      //第一个then
      .then(res => {
          console.log(`resolve-then1:${res}`);//“不执行这里”
          return 1;
      }, err => {
          console.log(`reject-then1:${err}`);//=>reject-then1:出错啦
          return 1
      })
//第二个then
.then(res => {
    console.log(`resolve-then2:${res}`);//=>resolve-then2:1
    throw new Error('抛出一个错')
}, err => {
    console.log(`reject-then2:${err}`);//“不执行这里”
    throw new Error('抛出一个错')
})
//第三个then
.then(res => {
    console.log(`resolve-then3:${res}`);//“不执行这里”
}, err => {
    console.log(`reject-then3:${err}`);//=>reject-then3:Error: 抛出一个错
})
//第四个then
.then(res => {
    console.log(`resolve-then4:${res}`);//=>resolve-then4:undefined
}, err => {
    console.log(`reject-then4:${err}`);//“不执行这里”
})
//第五个then
.then(res => {
    console.log(`resolve-then5:${res}`);//=>resolve-then5:undefined
}, err => {
    console.log(`reject-then5:${err}`);//“不执行这里”
})

JavaScript

Copy

输出,具体位置,参见上面的代码

//=>reject-then1:出错啦
//=>resolve-then2:1
//=>reject-then3:Error: 抛出一个错
//=>resolve-then4:undefined
//=>resolve-then5:undefined

JavaScript

Copy

上面罗列了我能找到的所有返回值的情况
在MDN上关于返回值的介绍,解释的有点绕
javascript异步之Promise then和catch
我们分析一下p3
p3直接执行的reject(‘出错啦’),返回的是一个promise对象,状态为reject
所以第一个then执行的第二个参数方法,输出=>reject-then1:出错啦,然后return 1
我们看第二个then的输出,可以发现,第一个then的return值会作为下第二个then的回调函数的参数值,第二个then又执行了throw Error
第二个then的throw Error,使得第三个then下的Promise对象状态为reject,所以第三个then输出=>reject-then3:Error: 抛出一个错
第三个then没有返回值,也没有执行throw Error,结果导致第四个then输出=>resolve-then4:undefined
第四个then和第三个then一样,没有返回值,所以第五个then输出的结果和第四个一样=>resolve-then5:undefined

我们做一下总结

  1. 如果前一个Promise对象是resolve状态,则后一个Promise对象执行第一个参数方法(resolve)
  2. 如果前一个Promise对象是reject状态,则后一个Promise对象执行第二个参数方法(reject)
  3. 如果前一个Promise对象抛出异常(throw error),则后一个Promise对象执行第二个参数方法(reject)
  4. 如果前一个Promise对象返回具体的值,则此数值将作为后一个Promise对象的输入,执行第一个参数方法(resolve)
  5. 如果前一个Promise对象没有返回状态(resolve或者reject),也没有抛错(throw error),也没有返回具体数值,我们则认为它返回 了一个undefined,则undefined将作为后一个Promise对象的输入,执行第一个参数方法(resolve)

关于p3 我们介绍到这里,回头看一下前面的栗子

Promise.reject('出错啦')
    .then(res => console.log(`res1:${res}`), err => console.log(`err1:${err}`))
    .then(res => console.log(`res2:${res}`), err => console.log(`err2:${err}`));

JavaScript

Copy

现在看来似乎简单了
因为前面是reject状态,所以第一个then执行第二个参数方法 “err => console.log(err1:${err})”
因为第一个then方法,没有返回状态,没有抛错,没有返会具体值,所以返回的是undefined,第二个then执行
“res => console.log(res2:${res}

catch

花了大量篇幅介绍then方法,其实then方法懂了,catch自然也就明白了,因为,catch就是then方法的语法糖
catch方法是.then(null, rejection)或.then(undefined, rejection)的别名
也就是说,catch也是then,它用于捕获错误,它的参数也就是then的第二个参数。
我们将上面的栗子做一下改动

const p4 = new Promise((resolve, reject) => {
    reject('出错啦');
})
.catch(err => {
    console.log(`catch1:${err}`);//=>catch1:出错啦
    return 1;
})
.then(res => {
    console.log(`then1:${res}`);//=>then1:1
    throw new Error('抛出一个错')
})
.catch(err => {
    console.log(`catch2:${err}`);//=>catch2:Error: 抛出一个错
})
.then(res => {
    console.log(`then2:${res}`);//=>then2:undefined
})

JavaScript

Copy

如果上面的关于then的介绍看懂了,这个自然就会明白

关于promise的then和catch就介绍这么多

END

参考链接
ES6 Promise——then与catch的返回值实践
ES6关于Promise的用法
promise的.then返回的一个新promise,他的状态和值,问题好大
Promise.prototype.then()