小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
从几个前端面试题再学Promise
1. 以下代码的输出是什么?
Promise.resolve(10).then(res => {
console.log(res)
}).catch(error => {
console.log(error)
}).then(res => {
console.log(res)
})
Promise.resolve(10).then(res => {
console.log(res)
}).then(res => {
console.log(res)
}).catch(error => {
console.log(error)
})
Promise.reject(10).then(res => {
console.log(res)
}).catch(error => {
console.log(error)
}).then(res => {
console.log(res)
})
Promise.reject(10).then(res => {
console.log(res)
}).then(res => {
console.log(res)
}).catch(error => {
console.log(error)
})
一开始以为弄懂了,说完答案之后面试官都给整乐了。
面试官:老哥,你回去再好好了解一下Promise的原理。
这题的知识点包括:
- 因为
Promise.prototype.then和Promise.prototype.catch方法返回的是 promise, 所以它们可以被链式调用。 - 如果针对某个状态的回调函数参数为空(null,undefined),MDN上是这么描述的。
注意:如果忽略针对某个状态的回调函数参数,或者提供非函数 (nonfunction) 参数,那么
then方法将会丢失关于该状态的回调函数信息,但是并不会产生错误。如果调用then的Promise的状态(fulfillment 或 rejection)发生改变,但是then中并没有关于这种状态的回调函数,那么then将创建一个没有经过回调函数处理的新Promise对象,这个新Promise只是简单地接受调用这个then的原Promise的终态作为它的终态。
可以理解为
如果没有传入相应的回调函数,Promise会有默认的处理。
返回一个和原Promise终态相同的新Promise。
- then()的返回值和then()中传入的回调函数有关。 这里不特指接受或拒绝的回调函数,可以一起讨论。
| then() 中的回调函数 | then 返回的 Promise |
|---|---|
| 返回了一个值 | 那么 then 返回的 Promise 将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。 |
| 没有返回任何值 | 那么 then 返回的 Promise 将会成为接受状态,并且该接受状态的回调函数的参数值为 undefined。 |
| 抛出一个错误 | 那么 then 返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。 |
返回一个已经是接受状态(fulfilled)的 Promise | 那么 then 返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。 |
返回一个已经是拒绝状态(rejected)的 Promise | 那么 then 返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。 |
返回一个未定状态(pending)的 Promise | 那么 then 返回 Promise 的状态也是未定的,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。 |
- catch()是特殊的then()
catch() 方法返回一个Promise (en-US),并且处理拒绝的情况。它的行为与调用
Promise.prototype.then(undefined, onRejected)相同。 (事实上, callingobj.catch(onRejected)内部callsobj.then(undefined, onRejected)).
通过以上的性质,我们可以解答刚刚的问题了
Promise.resolve(10).then(res => {
console.log(`res00 => ${res}`)
}).catch(error => {
console.log(`error => ${error}`)
}).then(res => {
console.log(`res01 => ${res}`)
})
// res00 => 10
// res01 => undefined
/*
1. resolve()返回一个fulfilled状态的promise
2. 第一个then()中注册了fulfilled状态的回调函数,
打印 res00 => 10
因为函数没有返回值
所以返回一个promise(fulfilled,undefined) => 知识点3
3. catch中没有注册fulfilled状态的回调函数,
所以返回一个promise(fulfilled,undefined) => 知识点2
4. 第二个then()中注册了fulfilled状态的回调函数,
打印 res01 => undefined
因为函数没有返回值
所以返回一个promise(fulfilled,undefined) => 知识点3
*/
Promise.resolve(10).then(res => {
console.log(`res00 => ${res}`)
}).then(res => {
console.log(`res01 => ${res}`)
}).catch(error => {
console.log(`error => ${error}`)
})
// res00 => 10
// res01 => undefined
/*
1. resolve()返回一个fulfilled状态的promise
2. 第一个then()中注册了fulfilled状态的回调函数,
打印 res00 => 10
因为函数没有返回值
所以返回一个promise(fulfilled,undefined) => 知识点3
3. 第二个then()中注册了fulfilled状态的回调函数,
打印 res01 => undefined
因为函数没有返回值
所以返回一个promise(fulfilled,undefined) => 知识点3
4. catch中没有注册fulfilled状态的回调函数,
所以返回一个promise(fulfilled,undefined) => 知识点2
*/
Promise.reject(10).then(res => {
console.log(`res00 => ${res}`)
}).catch(error => {
console.log(`error => ${error}`)
}).then(res => {
console.log(`res01 => ${res}`)
})
// error => 10
// res01 => undefined
/*
1. reject()返回一个rejected状态的promise
2. 第一个then()中注册了fulfilled状态的回调函数,
没有注册rejected状态的回调函数,
所以返回一个promise(rejected,10) => 知识点2
3. catch中注册了rejected状态的回调函数,
打印 error => 10
因为函数没有返回值
所以返回一个promise(fulfilled,undefined) => 知识点3
4. 第二个then()中注册了fulfilled状态的回调函数,
打印 res01 => undefined
因为函数没有返回值
所以返回一个promise(fulfilled,undefined) => 知识点3
*/
Promise.reject(10).then(res => {
console.log(`res00 => ${res}`)
}).then(res => {
console.log(`res01 => ${res}`)
}).catch(error => {
console.log(`error => ${error}`)
})
// error => 10
/*
1. reject()返回一个rejected状态的promise
2. 第一个then()中注册了fulfilled状态的回调函数,
没有注册rejected状态的回调函数,
所以返回一个promise(rejected,10) => 知识点2
3. 第二个then()中注册了fulfilled状态的回调函数,
没有注册rejected状态的回调函数,
所以返回一个promise(rejected,10) => 知识点2
3. catch中注册了rejected状态的回调函数,
打印 error => 10
因为函数没有返回值
所以返回一个promise(fulfilled,undefined) => 知识点3
*/
2. 同时发出10个请求,全部成功之后做一些处理
这一题可以用 Promise.all
Promise.all([p1,p2,p3])
3. 有一个请求,如果5s内有响应就返回响应,5s内没有响应就返回超时。
这一题可以用 Promise.race()
function func(p1, ms = 5000){
let p = new Promise((resolve,reject)=>{
let t = setTimeout(()=>{
resolve()
},ms)
})
return Promise.race(p1,p)
}