Promise的三种状态

constructor

我们知道Promise有三种状态:

  1. pending
  2. resolved(fulfilled)
  3. rejected

  这三种状态唯一,要么是pending、要么是resolved(fullfiled)、要么是rejected,所以我们在实现的时候需要定义类的三种状态。
  除此之外,Promise的构造函数需要我们传入一个函数作为参数,而且这个函数被分为resolve和reject来进行决议

class MyPromise {private status: 'pending' | 'resolved' | 'rejected' = 'pending';constructor(fn: Function) {if (typeof fn !== 'function') {throw Error(`Promise resolver ${fn} is not a function`)}fn(this.resolved, this.rejected)}
}

  为什么我要让fn立即执行并传入两个参数呢?这里我们可以试试原生Promise。

let promise = new Promise((resolve, reject) => resolve(123));
console.log(promise )


  可以看到Promise被new出来就被决议了,所以这里必然会传入两个参数。一个是决议成功的处理函数,一个是决议失败的处理函数。

同步的resolved处理函数

  决议成功函数主要用于改变MyPormise的状态为resolved,以及保存决议的结果。因为这个决议结果我们会在then中使用,所以使用this进行保存。

private resolved = (data: any): any => {// pending => resolvedif (this.status === 'pending') {this.data = data;this.status = 'resolved';}};

同步的rejected处理函数

与上面同步的resolved处理函数类似,只不过这个处理函数是将状态改为rejected

private resolved = (data: any): any => {// pending => resolvedif (this.status === 'pending') {this.data = data;this.status = 'rejected';}};

then函数

在书写then函数之前,我们需要知道then函数的功能。

  1. then函数接收两个参数,并且这两个参数都是带有一个参数的函数,前者为决议成功时的函数,后者为决议失败时的函数,而且第二个参数是可选参数。
  2. then函数返回一个Promise,若传入的函数并没有返回值则将undefined作为下一个Promise的data;若传入的函数返回值是一个普通值(非Promise),则将这个值作为下一个Promise的data;若传入的函数返回值是一个Promise,则等待这个Promise的决议完成,再将这个决议结果作为下一个Promise的返回值。

知道了这些功能,我们就可以来书写代码了。

public then = (onResolved: Function, onRejected?: Function): MyPromise => {// resolved的处理函数if (this.status === "resolved") {return new MyPromise((resolved: Function, rejected?: Function) => {// 这个值返回值是有用的,保存下来let res: any = onResolved(this.data);// 如果返回值是MyPromise的实例,则等待这个MyPromise决议if (res instanceof MyPromise) {res.then(resolved, rejected)} else {// 如果返回的是普通值,则直接决议resolved(res)}})// rejected的处理函数} else if (this.status === "rejected") {return new MyPromise((resolved: Function, rejected?: Function) => {let res: any = onRejected(this.data);if (res instanceof MyPromise) {res.then(resolved, rejected)} else {rejected(res)}})// 处理异步,什么也没变化的处理函数。后面再处理} else if (this.status === "pending") {}};

  如果各位写的代码没错的话,处理同步决议问题,是没错的。但是处理异步决议问题,则会一直处于rejected或resolved状态,因为MyPromise的函数执行时要等待决议的完成,但是因为这个是异步决议,这个决议会在调用栈尾部进行决议,所以他们双方互相等待。

处理异步决议问题

要想处理异步决议,我们就必须获得异步调用栈的所有函数,然后在异步执行完成之后进行决议。

// 异步调用栈的临时储存数组
private resolvedArray: Function[] = [];
private rejectedArray: Function[] = [];protected resolved = (data: any): any => {// pending => rejectedif (this.status === 'pending') {this.data = data;this.status = 'resolved';// 同步决议完成之后,执行异步队列的决议函数this.resolvedArray.forEach((fn: Function) => fn())}
};protected rejected = (data: any): any => {// pending => rejectedif (this.status === 'pending') {this.data = data;this.status = 'rejected';this.rejectedArray.forEach((fn: Function) => fn())}
};public then = (onResolved: Function, onRejected?: Function): MyPromise => {.....else if (this.status === "pending") {// 现在我不知道这个的状态,所以要当异步完成之后,再决议这个MyPromise,所以要把这个MyPromise存起来return new MyPromise((resolved: Function, rejected: Function) => {// 这里通过立即执行函数,得到外部的决议函数,在内部返回调用这个决议函数(通过闭包)this.resolvedArray.push(((onResolved: Function) => {return () => {let res: any = onResolved(this.data);if (res instanceof MyPromise) {res.then(resolved, rejected)} else {resolved(res)}}})(onResolved));this.rejectedArray.push(((onRejected: Function) => {return () => {let res: any = onRejected(this.data);if (res instanceof MyPromise) {res.then(resolved, rejected)} else {rejected(res)}}})(onRejected));})}
}

这样来看,代码完整了很多,但是仍然不够完整,我们可以测试一下代码:

let mp = new MyPromise(function (resolve, reject) {resolve('2');console.log('1')
});
mp.then(data => console.log(data));
console.log('3')

以正常的Promise运行流程,上述结果的是1 3 2,但是我们测试自己的代码时,不出意外是下面的结果:

为什么会造成上述的结果呢?因为我们在执决议函数resolvedrejected的时候是同步决议的,就会造成决议和实例化MyPromise时的时间是一致的,所以我们需要给决议函数加上setTimeout进行限制。

protected resolved = (data: any): any => {// pending => rejected// 利用setTimeout实现内部的异步晚于外部的同步setTimeout(() => {if (this.status === 'pending') {this.data = data;this.status = 'resolved';// 同步决议完成之后,执行异步队列的决议函数this.resolvedArray.forEach((fn: Function) => fn())}})
};protected rejected = (data: any): any => {// pending => rejectedsetTimeout(() => {if (this.status === 'pending') {this.data = data;this.status = 'rejected';this.rejectedArray.forEach((fn: Function) => fn())}})
};

这样就可以完成正常Promise的执行流程。

catch和finally的实现

catch即当决议失败时的特殊处理函数。

// catch即返回没有第一个参数的this.then的调用
public catch = (onRejected: Function): MyPromise => {return this.then(null, onRejected)
};
// finally即两个参数都有的决议
public finally = (fn: Function): MyPromise => {return this.then((value: any) => this.resolved(fn(value)),(err: any) => this.rejected(fn(err)));
};

源码如下:

/*** Created by 16609 on 2019/9/19**/
class MyPromise {// promise的三种状态,默认是pendingprivate status: 'pending' | 'resolved' | 'rejected' = 'pending';// promise的数据private data: any = null;// 异步resolve队列private resolvedArray: Function[] = [];private rejectedArray: Function[] = [];private resolved = (data: any): any => {// pending => resolved// @ts-ignore// 利用setTimeout实现内部的异步晚于外部的同步setTimeout(() => {if (this.status === 'pending') {this.data = data;this.status = 'resolved';// resolved时,执行异步resolved队列的所有函数,使所有MyPromise决议this.resolvedArray.forEach((fn: Function) => fn())}}, 0)};protected rejected = (data: any): any => {// pending => rejected// @ts-ignoresetTimeout(() => {if (this.status === 'pending') {this.data = data;this.status = 'rejected';this.rejectedArray.forEach((fn: Function) => fn())}}, 0)};public then = (onResolved: Function, onRejected?: Function): MyPromise => {// resolved的处理函数if (this.status === "resolved") {return new MyPromise((resolved: Function, rejected?: Function) => {// 这个值是有用的下保存下来let res: any = onResolved(this.data);// 每一个then返回的data如果是常值的话那么其下一个then的状态默认是resolved// 每一个then返回的promise,若promise是resolved,则下一个then的状态是resolved,否则是rejected// 如果返回的res是MyPromise的实例的话,那么执行这个MyPromise进行决议看决议之后的状态,那么这个状态就是下一个then的状态if (res instanceof MyPromise) {res.then(resolved, rejected)} else {// 如果返回的是普通值,则直接决议resolved(res)}})// rejected的处理函数} else if (this.status === "rejected") {return new MyPromise((resolved: Function, rejected?: Function) => {// 这个值是有用的下保存下来let res: any = onRejected(this.data);if (res instanceof MyPromise) {res.then(resolved, rejected)} else {rejected(res)}})// 处理异步,什么也没变化的处理函数} else if (this.status === "pending") {// 现在我不知道这个的状态,所以要当异步完成之后,再决议这个MyPromise,所以要把这个MyPromise存起来return new MyPromise((resolved: Function, rejected: Function) => {// 这里通过立即执行函数,得到外部的决议函数,在内部返回这个决议(通过闭包)this.resolvedArray.push(((onResolved: Function) => {return () => {let res: any = onResolved(this.data);if (res instanceof MyPromise) {res.then(resolved, rejected)} else {resolved(res)}}})(onResolved));this.rejectedArray.push(((onRejected: Function) => {return () => {let res: any = onRejected(this.data);if (res instanceof MyPromise) {res.then(resolved, rejected)} else {rejected(res)}}})(onRejected));})}};public catch = (onRejected: Function): MyPromise => {return this.then(null, onRejected)};public finally = (fn: Function): MyPromise => {return this.then((value: any) => this.resolved(fn(value)),(err: any) => this.rejected(fn(err)));};constructor(fn: Function) {if (typeof fn !== 'function') {throw Error(`Promise resolver ${fn} is not a function`)}fn(this.resolved, this.rejected)}
}

如果有优化的方案还请指正,多谢。

面试重点——promise原理以及实现相关推荐

  1. 面试重点-vue原理

    数据驱动视图 传统组件,只是静态渲染,更新还要依赖于操作DOM 数据驱动视图-Vue MVVM 数据驱动视图-React setState (暂时按下不表) Vue MVVM(模型视图视图模型) 总结 ...

  2. 2011年9月19日 面试重点:asp.net运行原理和生命周期

    面试重点:asp.net运行原理和生命周期 1.ispostback回调机制 isPostBack=false !IsPostBack==ture 当前页面是第一次加载 IsPostBack 由于用户 ...

  3. 「mysql优化专题」90%程序员面试都用得上的索引优化手册(5)【面试重点】

    本专题讲到索引查询优化,恭喜你,已经达到mysql优化的中级水平.这篇我们要讲的是mysql优化中重点中的重点--索引优化.面试官百分百必问 目录 多关于索引,分为以下几点来讲解: 一.索引的概述(什 ...

  4. promise原理—一步一步实现一个promise

    promise特点 一个promise的当前状态只能是pending.fulfilled和rejected三种之一.状态改变只能是pending到fulfilled或者pending到rejected ...

  5. 大数据面试重点之kafka(六)

    大数据面试重点之kafka(六) Kafka分区分配算法 可回答:Kafka的partition分区策略问过的一些公司:阿里云,小米参考答案: 1.生产者分区分配策略 生产者在将消息发送到某个Topi ...

  6. 面试大厂不看这两份Java面试核心知识点原理篇+框架篇,有个屁用?食屎啦泥?

    前言 面试在即,Java知识点很凌乱? 别急,有本套书在呢! 除了原理,还有框架! ★ 精细讲解JVM原理.Java基础.并发编程.数据结构和算法.网络与负载均衡 ★ 深入挖掘数据库与分布式事务.分布 ...

  7. js中promise原理及手动基本实现_V1

    前言 这几天面试过程,有个面试官突然跟我抠上了promise的实现原理,虽然有所准备,但是没能清晰地说出其中的原理,所以有点遗憾!!!,但是事已至此,只能默默去查了相关资料深入其中了解一番.因此就有了 ...

  8. 微型计算机应用重点,微型计算机原理以及的应用考试重点.doc

    微型计算机原理以及的应用考试重点 微型计算机原理以及应用 第一章: 1.微机的主要的特点是:(1)体积小.重量轻:(2)价格低廉:(3)可靠性高.结构灵活 (4)应用面广 2.微型机的分类: 按微处理 ...

  9. 大数据面试重点之kafka(七)

    大数据面试重点之kafka(七) Kafka的分区器.拦截器.序列化器? 问过的一些公司:ebay 参考答案: Kafka中,先执行拦截器对消息进行相应的定制化操作,然后执行序列化器将消息序列化,最后 ...

最新文章

  1. python中没有arcpy怎么办_Arcpy学习笔记(一)—无中生有(上)
  2. 科普MinGW MinGW-W64
  3. 2.3.5 操作系统之信号量机制实现进程的互斥、同步与前驱关系
  4. 在Flex中使用本地共享对象
  5. 每天一道LeetCode-----只可能有'.'和'*'的字符串正则匹配
  6. java中的可检查和不检查_检查Java测试中发生了什么
  7. caffe专题五——回归中——检测框架
  8. 看完这篇Redis缓存三大问题,够你和面试官battle几回合了
  9. 数据分析在内容运营中的作用
  10. 海南自贸区电信行业环境分析
  11. CentOS 7账号密码忘了怎么办?
  12. 作业1丨创建问答式简历程序
  13. javascript(一)
  14. 【贪心+堆/模拟费用流增广】BZOJ4946 [NOI2017]蔬菜
  15. [books] - SICP 2nd edition
  16. python语言基本认识_Python学习之认知(一)
  17. 基于Javaweb实现的人脸识别+GPS定位考勤系统
  18. Java设计模式-单一职责原则
  19. python-打气球
  20. android之SDK

热门文章

  1. iOS debug神器
  2. js 解析json数据实现快递包裹的查询
  3. mobiscroll插件(设置时间范围)
  4. pytorch波士顿房价预测模型
  5. Cheeses and bread
  6. 【硬核分享】Git 客户端基本使用及基础常见问题
  7. 倍洽福利 | 满满惊喜 倍洽开启双十一模式
  8. Nodejs实现微信网页授权及正确配置JS-SDK接口
  9. 批量修改文件名中的一部分,图文教程
  10. 9 年代码生涯,清华女硕士为何选择回长发展?