【手写promise——基本功能、链式调用、promise.all、promise.race】

文章目录

  • 前言
  • 一、前置知识
  • 二、实现基本功能
  • 二、实现链式调用
  • 三、实现Promise.all
  • 四、实现Promise.race
  • 总结


前言

关于动机,无论是在工作还是面试中,都会遇到Promise的相关使用和原理,手写Promise也有助于学习设计模式以及代码设计。
本文主要介绍了如何使用自己的代码去实现Promise的一些功能。


以下是本篇文章正文内容

一、前置知识

手写之前,需要知道promise有哪些基本特性:

1、promise有三种状态:pending、fulfilled、rejected。

2、promise的默认状态是pending,状态流转有两种:pending—>fulfilled、pending—>rejected。
在这里插入图片描述

3、实例化Promise时,可以接收一个函数作为参数,这个函数有两个参数,分别是resolve、reject方法用于改变实例状态。并能够执行下一个then中的回调函数。
4、then方法中接收两个函数作为参数,第一个函数相当于执行上一个 promise 中执行 resolve 后对应的回调,第二个函数相当于执行上一个 promise 中执行 reject 后对应的回调。

// 1. 实例化 Promise 时
// 可以接收一个函数作为参数,这个函数可以接收到 resolve 和 reject 两个实例方法
// 用于更改当前实例的状态,并把它们接收到的参数传递给下一个 then 对应的参数中
  new Promise((resolve, reject) => {
    // resolve 和 reject 可以都执行,但都执行的意义不大,因为 promise 状态发生更改后,就不能在被更改
    resolve('ok');
    // reject('err');
  }).then((value) => {
    console.log("resolve callback = ", value); // 若执行 resolve,则 value = ok
  }, (reason) => {
    console.log("reject callback = ", reason); // 若执行 reject,则 value = err
  });

5、then操作后会返回一个新的Promise,且支持链式调用。

let promise = new Promise((resolve,reject) => {
    resolve(11)
})
const a = new Promise((resolve,reject) => {
    reject('ok')
})
promise.then(res => {
    console.log(res)
    return a
}).then(res => console.log(res))

6、Promise.all以数组的形式接收多个Promise,当所有Promise执行完成,且状态都为fulfilled时,返回执行成功的结果;有一个失败,则返回失败的结果。

7、Promise.race以数组的形式接收多个Promise,只要有一个Promise先执行成功,无论什么状态,都返回这个结果,给到下一个then中对应的回调。

二、实现基本功能

1、

  • new 操作
  • resolve、reject方法
  • then方法
// 定义三种状态
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {

         // 1、默认状态 - PENDING
         this.status = PENDING;
         // 2、内部维护的变量值
         this.value = undefined;
         this.reason = undefined;

        try {
            executor(this.resolve.bind(this), this.reject.bind(this)) // 实例化传进来的函数会立即执行
        } catch(err) {
            this.reject(err)
        }
    }
    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
        }
    }
    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
        }
    }
    then(onFulfilled, onRejected) {
        if (onFulfilled && this.status === FULFILLED) {
            let res = onFulfilled(this.value)
            this.resolvePromise(res, resolve, reject)
        }
        if (onRejected && this.status === REJECTED) {
            let res = onRejected(this.reason)
            this.resolvePromise(res, resolve, reject)
        }        
    }
}

通过以下代码自测:

new Promise((resolve,reject) => {
    resolve(1)
}).then(res=> {
	console.log(res) // 1
})

2、考虑异步的情况。
以上代码都是同步的,Promise实例化时传入了异步函数如setTimeout,在setTimeout中resolve,在下一个then的回调函数获取异步函数的状态呢?

我们可以使用回调队列缓存起来,等待执行:

// 定义三种状态
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {

         // 1、默认状态 - PENDING
         this.status = PENDING;
         // 2、内部维护的变量值
         this.value = undefined;
         this.reason = undefined;
         this.onResolveCallBack = [];// 缓存 onResolve 
    	 this.onRejectCallBack = [];// 缓存 onReject 

        try {
            executor(this.resolve.bind(this), this.reject.bind(this)) // 实例化传进来的函数会立即执行
        } catch(err) {
            this.reject(err)
        }
    }
    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
            // 遍历调用 onResolveCallBack
      		this.onResolveCallBack.forEach(fn => fn());
        }
    }
    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
            this.onResolveCallBack.forEach(fn => fn());
        }
    }
    then(onFulfilled, onRejected) {
        if (onFulfilled && this.status === FULFILLED) {
            let res = onFulfilled(this.value)
            this.resolvePromise(res, resolve, reject)
        }
        if (onRejected && this.status === REJECTED) {
            let res = onRejected(this.reason)
            this.resolvePromise(res, resolve, reject)
        }
        // 当前 promise 状态为 pending,把当前的 onResolve & onReject 缓存起来
        if (this.status === PENDING) {
        	this.onResolveCallBack.push(() => {
        		onResolve(this.value);
        	});	
		    this.onRejectCallBack.push(() => {
		      onReject(this.value);
		    });
  		}       
    }
}

用以下代码测试通过:

 let p = new MyPromise((resolve, reject) => {
      setTimeout(()=>{
        resolve(1);
      },1000);
    }).then(value => {
      console.log('then resolve = ', value);
    }, reason => {
      console.log('then reject = ', reason);
    });
// then resolve = 1

二、实现链式调用

then(onFulfilled, onRejected) {
   	return new Promise((resolve, reject) => { // 支持链式调用
         if (onFulfilled && this.status === FULFILLED) {
             let res = onFulfilled(this.value)
             resolve(res);
         }
         if (onRejected && this.status === REJECTED) {
             let res = onRejected(this.reason)
             resolve(res);
         }
         if (this.status === PENDING) {
             this.onResolvedCallbacks.push(() =>{
                 let res = onFulfilled(this.value)
                 resolve(res);
             })
             this.onRejectedCallbacks.push(() => {
                 let res = onRejected(this.reason)
                 resolve(res);
             })
         }
     })   
 }

测试用例:

let promise = new Promise((resolve,reject) => {
    resolve(1)
})
promise.then(res => {
    return 2 // 返回普通对象
}).then(res => console.log(res)) // 2

如果在then中返回的是普通对象,上述代码能满足,如果返回的是一个Promise,则还需要补充:

then(onFulfilled, onRejected) {
    return new Promise((resolve, reject) => { // 支持链式调用
         if (onFulfilled && this.status === FULFILLED) {
             let res = onFulfilled(this.value)
             this.resolvePromise(res, resolve, reject)
         }
         if (onRejected && this.status === REJECTED) {
             let res = onRejected(this.reason)
             this.resolvePromise(res, resolve, reject)
         }
         if (this.status === PENDING) {
             this.onResolvedCallbacks.push(() =>{
                 let res = onFulfilled(this.value)
                 this.resolvePromise(res, resolve, reject)
             })
             this.onRejectedCallbacks.push(() => {
                 let res = onRejected(this.reason)
                 this.resolvePromise(res, resolve, reject)
             })
         }
     })
     
 }
 resolvePromise(res, resolve, reject) {
     if(res instanceof Promise) { // 对then中返回Promise做处理
         res.then(resolve, reject)
       } else{
         // 普通值
         resolve(res)
     }
 }

测试用例:

let promise = new Promise((resolve,reject) => {
    resolve(11)
})
const a = new Promise((resolve,reject) => {
    reject('ok')
})
promise.then(res => {
   return a;
}).then(res => console.log(res)) // ok

三、实现Promise.all

由上述前置知识可知,Promise.all是全部成功才返回成功的结果,有一个失败就返回失败的结果:

Promise.all = function(arr) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(arr)) {
            return reject("参数必须为数组");
        }
        let successNum = 0; // 成功的Promise条数
        let resultArray = [];
        let totalNum = arr.length; // 总Promise数量
        let isFail = false;
        arr.forEach(item => {
            item.then(res => {
                successNum++;
                resultArray.push(res);
                if (successNum === totalNum) { // 说明是全部成功
                    return resolve(resultArray)
                }
            }, err => {
               if (!isFail) reject(err)
               isFail = true;
            })
        });       
    })
}

测试用例:

function wait500(input) {
	return new Promise((resolve, reject) => {
		setTimeout(()=> {
			resolve(500)
		}, 500)
	})
}
function wait1000(input) {
	return new Promise((resolve, reject) => {
		setTimeout(()=> {
			resolve(1000)
		}, 1000)
	})
}
// 全部执行完成之后回调
Promise.all([wait1000(), wait500()]).then(result => {
	console.log('all end', result)
}, err => {
    console.log('fafaf', err)
}) // 'all end [500, 1000]'

四、实现Promise.race

由上述前置知识可知,Promise.race是有一个promise先执行成功,无论什么状态,都返回这个结果:

Promise.race = function(arr) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(arr)) {
            return reject("参数必须为数组");
        }
        arr.forEach(item => {
            item.then(res => {
                return resolve(res); // 状态不可逆,只要其中一个Promise转变了状态,后续状态就不会发生改变
            }, err => {
                reject(err);
            })
        });       
    })

}

测试用例:

function wait500(input) {
	return new Promise((resolve, reject) => {
		setTimeout(()=> {
			resolve(500)
		}, 500)
	})
}
function wait1000(input) {
	return new Promise((resolve, reject) => {
		setTimeout(()=> {
			resolve(1000)
		}, 1000)
	})
}
Promise.race([wait1000(), wait500()]).then(result => {
	console.log('all end', result)
}, err => {
    console.log('fafaf', err)
}) // 打印出'all end 500'

总结

总的代码:

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {

         // 1、默认状态 - PENDING
         this.status = PENDING;
         // 2、内部维护的变量值
         this.value = undefined;
         this.reason = undefined;

         // 存放回调
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        try {
            executor(this.resolve.bind(this), this.reject.bind(this))
        } catch(err) {
            this.reject(err)
        }
    }
    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
            this.onResolvedCallbacks.forEach(fn => fn())
        }

    }
    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
            this.onRejectedCallbacks.forEach(fn => fn())
        }
    }
    then(onFulfilled, onRejected) {
        return new Promise((resolve, reject) => { // 支持链式调用
            if (onFulfilled && this.status === FULFILLED) {
                let res = onFulfilled(this.value)
                this.resolvePromise(res, resolve, reject)
            }
            if (onRejected && this.status === REJECTED) {
                let res = onRejected(this.reason)
                this.resolvePromise(res, resolve, reject)
            }
            if (this.status === PENDING) {
                this.onResolvedCallbacks.push(() =>{
                    let res = onFulfilled(this.value)
                    this.resolvePromise(res, resolve, reject)
                })
                this.onRejectedCallbacks.push(() => {
                    let res = onRejected(this.reason)
                    this.resolvePromise(res, resolve, reject)
                })
            }
        })
        
    }
    resolvePromise(res, resolve, reject) {
        if(res instanceof Promise) {
            res.then(resolve, reject)
          } else{
            // 普通值
            resolve(res)
        }
    }
}
// 全部成功才返回成功的结果,有一个失败就返回失败的结果
Promise.all = function(arr) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(arr)) {
            return reject("参数必须为数组");
        }
        let successNum = 0;
        let resultArray = [];
        let totalNum = arr.length;
        let isFail = false;
        arr.forEach(item => {
            item.then(res => {
                successNum++;
                resultArray.push(res);
                if (successNum === totalNum) {
                    return resolve(resultArray)
                }
            }, err => {
                if (!isFail) reject(err)
                isFail = true;
            })
        });       
    })
}

// 某个promise先完成,
Promise.race = function(arr) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(arr)) {
            return reject("参数必须为数组");
        }
        arr.forEach(item => {
            item.then(res => {
                return resolve(res);
            }, err => {
                reject(err);
            })
        });       
    })

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/92651.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【五】sql 语言 -- 概览

SQL 语言概述SQL 语言提出和发展SQL 语言的功能概述利用 SQL 语言建立数据库学生选课数据库 SCT1. 定义数据库和表 SQL-DDL创建数据库的语句—Create Database创建关系/表的语句—Create Table 2. 向表中追加元组 SQL-DML 利用 SQL 语言进行简单查询单表查询 - SELECT-FROM-WHE…

MySQL安装记录

背景 Windows系统重装了, 想恢复一下之前的MySQL环境, 而且本地数据库也是比较常用的, 刚好本次也在安装, 做一个简单的记录. 也算是自己的学习记录输出. 遇到的问题当然也可以同时记录在这里, 方便后 续回顾. 资料包 百度网盘 // TODO 估计放了也会被CSDN屏蔽, 这里就不放…

Redis 的混合持久化

RDB 相比于 AOF,数据恢复的速度更快,因为是二进制数据,直接加载进内存即可,但是 RDB 的频率不好把握。 如果频率太低,在两次快照期间服务器发生宕机,可能会丢失较多的数据如果频率太高,频繁写入…

软件工程(十三) 设计模式之结构型设计模式(一)

前面我们记录了创建型设计模式,知道了通过各种模式去创建和管理我们的对象。但是除了对象的创建,我们还有一些结构型的模式。 1、适配器模式(Adapter) 简要说明 将一个类的接口转换为用户希望得到的另一个接口。它使原本不相同的接口得以协同工作。 速记关键字 转换接…

伦敦金短线好还是长线好

在伦敦金投之中,长期有一个争论很久的问题,那就是伦敦金投资究竟是长线好还是短线好?不同的投资者对这个问题有不同的看法,一般认为,伦敦金投资比较适合短线交易。笔者也将讨论这个问题,看看伦敦金投资是不…

Ubuntu Touch OTA-2 推出,支持 Fairphone 3 和 F(x)tec Pro1 X

导读UBports 基金会近日宣布为基于 Ubuntu 20.04 LTS (Focal Fossa) 的 Ubuntu Touch 移动操作系统发布并全面提供 OTA-2 软件更新。 Ubuntu Touch OTA-2 在首次 OTA 更新整整四个月后发布,支持新设备,包括 Fairphone 3、F(x)tec Pro1 X 和 Vollaphone X…

YOLO目标检测——肺炎分类数据集下载分享

肺炎分类数据集总共21000图片,可应用于:肺炎检测、疾病诊断、疾病预测和预警等等。 数据集点击下载:YOLO肺炎分类数据集21000图片.rar

基于openstack helm在kubernetes中部署单节点openstack

helm部署单节点openstack OpenStack-Helm 的目标是提供一系列 Helm charts,以便在 Kubernetes 上简单、弹性、灵活地部署 OpenStack 及相关服务。 OpenStack 可以在独立的 Kubernetes 集群之上运行,而不是在现有基础设施之上运行。在此设置中&#xff…

【小沐学Unity3d】3ds Max 骨骼动画制作(Physique 修改器)

文章目录 1、简介2、Physique 工作流程3、Physique 对象类型4、Physique 增加骨骼5、Physique 应用和初始化6、Physique 顶点子对象7、Physique 封套子对象8、设置关键点和自动关键点模式的区别8.1 自动关键点8.2 设置关键点 结语 1、简介 官方网址: https://help.…

计算机毕设 基于机器学习与大数据的糖尿病预测

文章目录 1 课题背景2 数据导入处理3 数据可视化分析4 特征选择4.1 通过相关性进行筛选4.2 多重共线性4.3 RFE(递归特征消除法)4.4 正则化 5 机器学习模型建立与评价5.1 评价方式的选择5.2 模型的建立与评价5.3 模型参数调优5.4 将调参过后的模型重新进行…

<C++>泛型编程-模板

1.泛型编程 如何实现一个通用的交换函数呢?可以使用函数重载 void Swap(int &left, int &right) {int temp left;left right;right temp; }void Swap(double &left, double &right) {double temp left;left right;right temp; }void Swap(c…

Sentinel流量控制与熔断降级

📝 学技术、更要掌握学习的方法,一起学习,让进步发生 👩🏻 作者:一只IT攻城狮 ,关注我,不迷路 。 💐学习建议:1、养成习惯,学习java的任何一个技术…

[MyBatis系列③]动态SQL

目录 1、简介 2、if标签 3、foreach标签 4、SQL抽取 ⭐MyBatis系列①:增删改查 ⭐MyBatis系列②:两种Dao开发方式 1、简介 开发中在MyBatis映射文件配置SQL语句,但是前面配置的都是比较简单的,不涉及稍复杂的业务场景。想要应…

函数式编程-Stream流学习第二节-中间操作

1 Stream流概述 java8使用的是函数式编程模式,如同它的名字一样,它可以用来对集合或者数组进行链状流式操作,让我们更方便的对集合或者数组进行操作。 2 案例准备工作 我们首先创建2个类一个作家类,一个图书类 package com.stream.model;…

【BUG】解决安装oracle11g或12C中无法访问临时位置的问题

项目场景: 安装oracle时,到第二步出现oracle11g或12C中无法访问临时位置的问题。 解决方案: 针对客户端安装,在cmd中执行命令:前面加实际路径setup.exe -ignorePrereq -J"-Doracle.install.client.validate.cli…

RHCE——八、DNS域名解析服务器

RHCE 一、概述1、产生原因2、作用3、连接方式4、因特网的域名结构4.1 拓扑4.2 分类4.3 域名服务器类型划分 二、DNS域名解析过程1、分类2、解析图:2.1 图:2.2 过程分析 三、搭建DNS域名解析服务器1、概述2、安装软件3、/bind服务中三个关键文件4、配置文…

原生微信小程序 动态(横向,纵向)公告(广告)栏

先看一下动态效果 Y轴滚动公告的原理是swiper组件在页面中的Y轴滚动,属性vertical,其余属性也设置一下autoplay circular interval"3000" X轴滚动的原理是,利用动画效果,将内容从右往左过渡过去 wxml: &l…

DBi Tech Studio Controls for .NET Crack

DBi Tech Studio Controls for .NET Crack Studio Controls for.NET为企业开发人员提供了一套全面的Windows布局和信息表示软件元素,面向搜索业务分析商业调度和UI表示控制器的程序员。Studio Controls for.NET包含17个免版税的.NET Windows窗体控件,用于…

高精度地图定位在高速公路自动驾驶系统中的应用

近年来随着汽车保有量不断增加,随之而来的是: ( 1) 严重的交通拥堵,通行效率低下,用在通行上的时间不断增加; ( 2) 交通事故频发,交通事故导致的伤亡人数和费用不断增加,而且绝大多数事故是由人为因素导致的; ( 3) 大气…

3.2 数据的表现形式及其运算

3.2.1 常量和变量 在计算机高级语言中,数据有两种表现形式:常量和变量。 1.常量 在程序运行过程中,其值不能被改变的量称为常量。如例3.1程序中的5,9,32和例3.2程序中的1000,0.0036,0.0225,0.0198是常量。数值常量就是数学中的常数。 常用的…
最新文章