【Web前端问题】求解Promise的一道面试题

最近在看Promise相关知识,遇到一个面试题,以我的理解,应该是先输出a failed,然后b failed b passed的,可是为什么a failed在中间输出了??
图片描述

回答:

强调:Promise的每个thencatch都是异步执行的。

因此,实际上最先执行的是a.then,但没有定义catch,所以抛出异常,然后异步交给后面的catch处理(a failed)。此时下一个等待执行的是b.catchb failed),处理完之后,同样异步交给后面的thenb passed)。接着,之前排队的catchb failed)执行,最后b passed执行。

这就是各个then/catch交替执行的原因。

整个过程类似于下面的代码:

setTimeout(function(){

console.log(1);

setTimeout(function(){

console.log(2);

}, 0);

}, 0);

setTimeout(function(){

console.log(3);

setTimeout(function(){

console.log(4);

}, 0);

}, 0);

结果打印1 3 2 4,而不是1 2 3 4

回答:

把代码换个形式看,

let p1 = Promise.reject('a')                // p1 rejected

let p2 = p1.then(function cb1 () {log('a passed')}) // then 未指定 onrejected;p2 pending

let p3 = p2.catch(function cb2 (){log('a failed')}) // p3 pending,且要等 p2 settled

let p4 = Promise.reject('b') // p4 rejected

let p5 = p4.catch(function cb3 (){log('b failed')}) // p5 pending

let p6 = p5.then(function cb4 (){log('b passed')}) // p6 pending,要等 p5 settled

p1 的状态是 rejected, 而 cb1 对应的是 onfullfilled,所以没机会进入 queue

所以 event loop 中 queue 的状态是
第一轮: [cb3]
第二轮: [cb2, cb4]

所以 b failed -> a failed -> b passed

回答:

catch 本质也是 Promise.prototype.then 的封装,所以 a 相当于跳过了一轮循环,整个过程可以这么理解

reject('a')

reject('b')

(next turn)

reject('a') -> handle(onReject) 没 handler,传递下去

reject('b') -> handle(onReject) 这里被 catch 处理

(next turn)

reject('a') -> handle(onReject) -> handle(onReject) 这里被 catch 处理

reject('b') -> handle(onReject) -> handle(onFullfill)

回答:

不要被链式调用迷惑了。

let a1 = Promise.reject('a')

let a2 = a1.then(() => {

console.log('a passed')

})

let a3 = a2.catch(() => {

console.log('a failed')

})

let b1 = Promise.reject('b')

let b2 = b1.catch(() => {

console.log('b failed')

})

let b3 = b2.then(() => {

console.log('b passed')

})

不知道这样你能懂吗?链式调用的then()和catch()处理的不是同一个promise。
而未处理的状态会传递下去。

mdn上有解释的

注意:如果忽略针对某个状态的回调函数参数,或者提供非函数 (nonfunction) 参数,那么 then 方法将会丢失关于该状态的回调函数信息,但是并不会产生错误。如果调用 then 的 Promise 的状态(fulfillment 或 rejection)发生改变,但是 then 中并没有关于这种状态的回调函数,那么 then 将创建一个没有经过回调函数处理的新 Promise 对象,这个新 Promise 只是简单地接受调用这个 then 的原 Promise 的终态作为它的终态。

什么意思呢,then的第二个参数其实是能处理err的,但是没定义的话,就会将上一个promise的状态当做当前then创建的返回值promise的状态传递下去。

回答:

解题关键:
第一:

Promise.reject(reason)

Promise.resolve(value)

Promise.prototype.catch(onRejected)

Promise.prototype.then(onFulfilled, onRejected)

Promise.prototype.finally(onFinally)

前2个是静态方法,后面3个是原型方法(对象调用),他们的共同点就是都是返回一个新的promise对象。

第二:

onRejected,onFulfilled,onFinally 这个3个称为executor 函数,分别处理 promise的状态

1. onRejected 处理 Rejected

2. onFulfilled 处理 Fulfilled

3. onFinally 处理 Rejected或者Fulfilled

那么问题来了,当这个3个执行函数缺失的时候,Promise怎么处理呢?示例代码如下

Promise.reject(a)

.then()

.then()

.catch()

.finally(()=>{

console.log('test') // 会输出吗?会报错吗?很显然,不会报错,因为这些方法都会返回新的promise对象

})

猜测下,这个3个方法的底层实现,会不会是这样

Promise.prototype.then = function(onFulfilled,onRejected,resutl){

return new Promise((resolve,reject)=>{

if(typeof onFulfilled === 'function'){

resolve(onFulfilled.call(this,resutl));

}else{

resolve(resutl);

}

if(typeof onRejected === 'function'){

reject(onRejected.call(this,resutl));

}else{

reject(resutl);

}

});

}

所以这样,就好理解这个题目的输出了
第一个catch的执行函数执行,需要等第一个then的执行函数执行(虽然缺失,但是包装函数还是有的),而这个then的执行,需要等到Promise.reject() 完成 2
第二个catch的执行函数执行,需要等到Promise.reject() 完成。1
第二个then执行函数的执行,需要等到第二个catch的执行函数执行,第二个catch的执行需要等到Promise.reject() 完成。2
同时由于代码的执行先后的原因 所以第一个 2 会在第二个2 的前面,因此最终的执行顺序就是 1 2 2(第一个)(第二个)

修改上面的代码,可以很容易实现不同的输出方式。因为每个 执行函数的执行,都是需要等到自身promise对象状态发生变化才会去做的。

Promise.reject('a')

.then(success=>{

},err=>{

return new Promise((resolve,reject)=>{

})

})

.catch(e=>{

console.log('a failed') //这个永远不会执行

})

Promise.reject('b')

.catch(e=>{

console.log('b ...')

})

.then(success=>{

console.log('b then')

})

按照 event loop去理解也是可以,其实这也是异步的本质。如果按照 执行函数的执行一定是promise状态发生变化了才会触发,这样理解会不会更好

示例代码

回答:

此句是错的 catch对于已经完成的promise同步执行 此句是错的

then异步执行(类型setTimeout(xxx,0))

以上是 【Web前端问题】求解Promise的一道面试题 的全部内容, 来源链接: utcz.com/a/140865.html

回到顶部