【Web前端问题】求解Promise的一道面试题
最近在看Promise相关知识,遇到一个面试题,以我的理解,应该是先输出a failed,然后b failed b passed的,可是为什么a failed在中间输出了??
回答:
强调:Promise
的每个then
或catch
都是异步执行的。
因此,实际上最先执行的是a.then
,但没有定义catch
,所以抛出异常,然后异步交给后面的catch
处理(a failed
)。此时下一个等待执行的是b.catch
(b failed
),处理完之后,同样异步交给后面的then
(b passed
)。接着,之前排队的catch
(b 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 rejectedlet 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。
而未处理的状态会传递下去。
注意:如果忽略针对某个状态的回调函数参数,或者提供非函数 (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