【逐点突破系列】前端面试必备——异步(Promise)

let reject = (reason)=>{
if(this.status===‘pending’){
this.status = ‘rejected’
this.reason = reason
this.onRejected.forEach(fn=>fn(this.reason))
}
}
try{
executor(resolve,reject) //executor同步执行
}catch (e) {
reject(e)
}

}

then(onFulfilled, onRejected) { // 如果then的时候 根据当前状态执行成功或者失败
if(this.status===‘fulfilled’){
onFulfilled(this.value)
}
if(this.status===‘rejected’){
onRejected(this.reason)
}
if(this.status===‘pending’){
this.onFulfilled.push(onFulfilled) //发布订阅模式储存异步回调
this.onRejected.push(onRejected)
}
}

}

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’){
setTimeout(()=>{ // 为了保证promise2 已经产生了
try{
let x = onFufilled(this.value);
resolvePromise(promise2,x,resolve,reject);
}catch(e){

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-5wNks3BT-1715797272135)]

[外链图片转存中…(img-nsc8oTrO-1715797272135)]

[外链图片转存中…(img-fUIVCo5W-1715797272135)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值