做题学知识(4)之 Promise

257 阅读4分钟

问题

第一题

请问是打印出来错误 1 还是错误 2 呢?

new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('错误1')
    })
}).then(val => {
    throw '错误2'
}).catch(error => {
    console.log(error)
})

第二题

请问打印出来什么?

new Promise((resolve, reject) => {
    setTimeout(() => {
        reject(1)
    })
}).then(val => {
    console.log(2)
}, error => {
    console.log(3)
}).catch(error => {
    console.log(4)
})

第三题

请问是打印出来错误 1 还是错误 2 呢?

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    })
}).then(val => {
    throw 'error'
}, error => {
    console.log('错误1')
}).catch(error => {
    console.log('错误2')
})

答案

这几道题主要考察了 Promise 运行机制。仅仅是了解了 Promise 的各个语法糖,了解的不够细致答出来了也就属于猜对的。

简介 Promise

Promise 有三种状态,pending(初始状态),fulfilled(完成),rejected(拒绝)。状态更改一旦完成就不能在修改,Promise 的构造函数接受一个 executor(自执行)函数。自执行函数提供俩个值为函数的参数:resolve,reject。当 resolve 执行的时候状态变为 fulfilled,当 reject 执行的时候状态变为 fulfilled。

通过 Promise 创建的对象有 then、catch、finally 三个方法。then 接受俩个值为函数的参数: onFulfilled 和 onRejected 。当状态变为 fulfilled 的时候 onFulfilled 就会调用;当状态变为 rejected 的时候 onRejected 就会调用,并且返回值都是新的 Promise 创建的对象。catch 方法相当于 then(null, onRejected)。finally 是当状态无论变为啥都会执行的方法。

答案解析

第一题

new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('错误1')
    })
}).then(val => {
    throw '错误2'
}).catch(error => {
    console.log(error)
})

这个题只有一个 then,而 catch 本身调用的就是 then 的 onRejected 方法,因此可以缩写为这样:

new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('错误1')
    })
}).then(val => {
    throw '错误2'
}, error => {
    console.log(error)
})

这样就很明显了,then 提供的俩个函数 onFulfilled 和 onRejected。只有当状态 变为 fulfilled 的时候才会执行 onFilled。当状态变为 rejected 的时候才会执行 onRejected。构造函数只调用了 reject 函数。因此状态只能变为 rejected。所以 onFulfilled 压根就不会执行。因此答案是打印错误1

第二题

请问打印出来什么?

new Promise((resolve, reject) => {
    setTimeout(() => {
        reject(1)
    })
}).then(val => {
    console.log(2)
}, error => {
    console.log(3)
}).catch(error => {
    console.log(4)
})

这个题一打眼就能看出来会打印 3,纠结的是 4。这里 4 是不打印的,原因也很简单,当 then 的 onFulfilled 和 onRejected 其中有一个执行的时候就会返回新的 Promise。也就是 catch 方法已经换了挂载对象,而新的 Promise 又没有执行 reject 函数,所以肯定不会触发 catch。因此答案是只打印 3

第三题

请问是打印出来错误 1 还是错误 2 呢?

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    })
}).then(val => {
    throw 'error'
}, error => {
    console.log('错误1')
}).catch(error => {
    console.log('错误2')
})

Promise 的状态一但变更就不会在发生更改。构造函数中状态变成了 fulfilled 所以 then 中的函数 onFulfilled 会执行,而 onRejected 不会执行。所以错误1是肯定不会打印的。然后 then 中报错会被 try catch 捕获到,将新的 promise 的状态变为 rejected,因此最后的 catch 会被执行,打印出来错误 2。因此答案是错误 2

从这道题你也可以看出来 onFulfilled 中报的错误只有下一个 then 的 onRejected 能够捕获到,如果不写下一个 then。 onFulfilled 函数的报错在 node 环境中就会提示你没有提供 onRejected。而终止运行,因此建议大家多用 catch ,少用then 的第二个函数 onRejected。这样就不会出现少捕获错误的情况。

总结

Promise 作为新的异步解决方式不仅写法优雅而且提供的 API 都是常见的范式,例如:

  1. all:多个 Promise 并行触发,全部执行完成获得调用结果。
  2. race:多个 Promise 并行触发,其中一个执行完成立刻获得执行结果

因此 Promise 就特别流行,掌握它就成了必备技能之一。简单熟悉 API 可能你在项目中拿来直接用是没有问题的,但是只有深入理解他你才能够在各种面试中游刃有余的解答各种变态问题。

觉得看文章还不够过瘾,我创建了一个交流群,欢迎大家扫码关注公众号进行获取。