2024年前端最全【逐点突破系列】前端面试必备——异步(Promise,年薪50W

文章分享了前端面试中常见的问题,涵盖HTML、CSS、JavaScript等技术,重点解析Promise链式调用的实现原理和注意事项,以及A+规范中的相关内容,为求职者提供学习资源和面试准备指南。
摘要由CSDN通过智能技术生成

最后

我可以将最近整理的前端面试题分享出来,其中包含HTML、CSS、JavaScript、服务端与网络、Vue、浏览器、数据结构与算法等等,还在持续整理更新中,希望大家都能找到心仪的工作。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

篇幅有限,仅展示部分截图:

}

3.添加链式调用

3.1 链式特性

1.如果promise中的then方法,无论是成功还是失败,他的返回结果是一个普通的时候就会把这个结果传递给外层的then的下一个then的成功回调

Promise.reject().then((val)=>{
return ‘ok’
},()=>{
return ‘err’
}).then((val)=>{
console.log(‘ok’ + val)
},(e)=>{
console.log(‘err’ + e)
})
// okerr 第一个then失败的回调返回的是普通值,还是走第二个的then中成功回调

2.如果成功或者失败的回调的返回值 返回是一个promise 那么会让这个promise执行 采用他的状态

Promise.resolve().then(()=>{
return new Promise((resolve)=>{
setTimeout(()=>{
resolve(1)
},1000)
})
}).then((val)=>{
console.log(val)
})
//一秒后打印1

3.2实现

这一版主要是实现链式调用,稍微绕一点,但是理清楚了也不难
首先明确一下,then后面会返回一个新的Promise,所以才能执行链式调用
第一个比较绕的地方,怎么让第二个then里面的回调执行?只要调用then返回的新promise(promise2)时的resolve方法就行了
第二个比较绕的地方就是参数是什么?我们看特性3.1,参数是什么要根据第一个then中回调的返回值来判断,返回值如果是正常值,如果是Piomise,,所以我们封装一个resolvePromise的方法来处理,参数的话有第一个then的回调,新创建的promise2,以及promise2里面的resolve.reject

需要改变的核心代码如下
let resolvePromise = (promise2, x, resolve, reject) => {…}

class Promise {
construcotr(){…}
then(){
let promise2 = new promise((resolve,reject)=>{
let x = onFulfiled() // onFulfilef是第一个then中的回调函数
resolvePromise(promise2,x, resolve, reject)
})
return promiese2
}
}

resolvePromise这个方法会判断onFulfiled返回值类型,如果是普通值会怎么样,如果是一个Promise会怎么样,如果报错会怎么样,详细实现方法可以参考promise A+规范
完整实现

let resolvePromise = (promise2, x, resolve, reject) => {
// 监测到环形链
if(promise2===x) return new TypeError(‘chaining cycle detected for promise’)
if(typeof x ===‘function’ ||(typeof x ===‘object’ && x!==null)){
try{
//尝试取出then,有问题报错
let then = x.then
if(typeof then === ‘function’){ //这里是最绕的,想清楚promise2和x的关系,x.then会不会执行取决于使用者的逻辑,会不会在第一个then中回调函数中返回的promise中调用它的resolve改变状态
then.call(x,resolve,reject)
}else{// then不是function
resolve(x)
}
}catch (e) {
reject(e)
}
}else{ //普通类型
resolve(x)
}

}

class Promise {
constructor(executor) {
this.status = ‘pending’
this.value = undefined
this.reason = undefined
this.onFulfilledCallback = []
this.onRejectedCallback = []
let resolve = (value) => {
if (this.status === ‘pending’) {
this.status = ‘fulfilled’
this.value = value
this.onFulfilledCallback.forEach(fn => fn(this.value))
}

}
let reject = (reason) => {
if (this.status === ‘pending’) {
this.status = ‘rejected’
this.reason = reason
this.onRejectedCallback.forEach(fn => fn(this.reason))
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}

}

then(onFulfilled, onRejected) { // 如果then的时候 根据当前状态执行成功或者失败
let promise2 = new Promise((resolve, reject) => {
if (this.status === ‘fulfilled’) {
setTimeout(() => { //这里之所以异步是因为必须保证resolvePromise(promise2, x, resolve, reject)时Promise2创建完成
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.status === ‘rejected’) {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.status === ‘pending’) {
this.onFulfilledCallback.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})

})
this.onRejectedCallback.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
})
return promise2
}

}

基本面试5-10分钟代码写到这里,都能给满分通过,剩下的就是4个打补丁的地方了

4.打补丁

4.1 补丁点

实际上是A+规范测试用例的补丁,我按重要程度往下排,前面的必须做到能写出来(面试可以不写),后面的知道即可

  1. then的默认参数配置
  2. x可能是个Promise,它的返回值还可能是个Pormise,这个Promised的返回值还可能是个Promise…
  3. 调用Promise的resolve方法,如果参数是个promise怎么办 (这个不在A+规范里,但是新版promise实现了)
  4. 别人实现的可能不规范,我们的resolvePromise需要加一点限制,改变了状态就不能再变了 (这个在A+规范测试用例里,但是我感觉意义不大)

4.1.1 默认参数

Promise.resolve(1).then().then().then().then((val)=>{
console.log(val) //1
})
//失败也是类似的传递

可以默认传递一个回调函数

then(onFufilled,onRejected){
onFufilled = typeof onFufilled === ‘function’?onFufilled:value=>value;

}

4.1.2 x中promise嵌套

这个也不难,递归调用resolvePromise去解析

let resolvePromise = (promise2,x,resolve,reject) => {

then = x.then
/*这个是之前的核心代码 then.call(x,resolve,reject)
*实际等同于 then.call(x,(y)=>{

  •         resolve(y)   这个y是x作为promise的返回值,现在这个y可能是个promise所以再递归调用resolvePromise去解析
    
  •      },reject)  
    

*/
改成这样:
then.call(x,(y)=>{
resolvePromise((promise2,y,resolve,reject)
},reject)

}

4.1.3 resolve中是promise

constructor(executor){

let resolve = (value) =>{ // 如果resolve的值时一个promise
if(value instanceof Promise){
// 我就让这个promise执行,把成功的结果再次判断
return value.then(resolve,reject) //参数会自动传递到resolve里面去
}

}

5.添加方法

Promise比较重要的方法一共有五个方法

5.1 Promise.resovle

把一个对象包装成Promise对象,特别注意状态不一定是成功的
各种注意事项请看阮一峰es6-promise
直接记忆不好记忆,但是结合源码很简单,理所当然

static resolve(value){
return new Promise((resolve,reject)=>{
resolve(value);
})
}

5.2 Promise.reject

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

static reject(err){
return new Promise((resolve,reject)=>{
reject(err);
})
}

5.3 PromiseInstance.prototype.finally

这个是实例方法,其他几个都是类方法
无论成功还是失败都会调用,所以可定返回的也是一个Promimse,成功失败都会调用传入的回调,
finally不接受值,返回的Promise的状态受前一个promise状态的影响
finally如果在中间同时回调返回一个promise则会等待promise

Promise.resolve(1).finally(
(a)=>{
return new Promise((resolve)=>{
setTimeout(function () {
resolve(2)
},3000)
})

}
).then((data)=>{
console.log(data)
})

等待3秒后打印1

finally实现

Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};

5.4 Promise.race Promise.all

race和all一个是谁先调用谁执行后面then中的回调,一个是全部调用才执行后面then中的回调
他们都需要对参数中传入的数组进行遍历

all的实现需要借助计数器,这也是实现异步任务通知的一种方法
直接完成或者异步完成都会使计数器加1 当计数器和数组长度相等时就是all方法完成的时候,然后把结果数组传到下一个回调

race的实现就是,遍历数组中元素current,都去改变返回promise的值,谁先改变就取谁的值传到会带到函数里面

return promose((resolve,reject)=>{
if(isPromise(current)){
current.then(resolve,reject)
}else{
resolve(current)
}
})

具体实现见6

6.完整实现

let resolvePromise = (promise2,x,resolve,reject) => {
if(promise2 === x){
return reject(new TypeError(‘Chaining cycle detected for promise #’))
}
// 如果调用了失败 就不能再调用成功 调用成功也不能再调用失败
let called;
if(typeof x =‘function’ || (typeof x === ‘object’ && x! null) ){
try{
let then = x.then; // Object,dedefineProperty
if(typeof then === ‘function’){
then.call(x,(y)=>{ // x.then(y=>,err=>)
if(called) return;
called = true
// y有可能解析出来的还是一个promise
// 在去调用resolvePromise方法 递归解析的过程
// resolve(y)
resolvePromise(promise2,y,resolve,reject); // 总有y是普通值的时候
},e=>{
if(called) return;
called = true
reject(e);
})
}else{
if(called) return;
called = true
resolve(x);
}
}catch(e){
if(called) return;
called = true
reject(e);
}
}else{
if(called) return;
called = true
resolve(x); // ‘123’ 123
}
}
class Promise{
constructor(executor){
this.value = undefined;
this.reason = undefined;
this.status = ‘pending’;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) =>{ // 如果resolve的值时一个promise
// if(typeof value === ‘function’ || (typeof value == ‘object’&&value !== null)){
// if(typeof value.then == ‘function’){
// return value.then(resolve,reject)
// }
// }
if(value instanceof Promise){
// 我就让这个promise执行,把成功的结果再次判断
return value.then(resolve,reject) //参数会自动传递到resolve里面去
}
if(this.status === ‘pending’){
this.status = ‘fulfilled’
this.value = value;
this.onResolvedCallbacks.forEach(fn=>fn());
}
}
let reject = (reason) =>{
if(this.status === ‘pending’){
this.status = ‘rejected’
this.reason = reason;
this.onRejectedCallbacks.forEach(fn=>fn());
}
}
try{
executor(resolve,reject);
}catch(e){
console.log(e)
reject(e);
}
}
then(onFufilled,onRejected){
// 可选参数的配置
onFufilled = typeof onFufilled === ‘function’?onFufilled:value=>value;
onRejected = typeof onRejected === ‘function’?onRejected:err=>{throw err}
let promise2 = new Promise((resolve,reject)=>{
if(this.status === ‘fulfilled’){

最后

我可以将最近整理的前端面试题分享出来,其中包含HTML、CSS、JavaScript、服务端与网络、Vue、浏览器、数据结构与算法等等,还在持续整理更新中,希望大家都能找到心仪的工作。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

篇幅有限,仅展示部分截图:

  • 25
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值