Promise是ES6出现的一个异步编程的一个解决方案,改善了以往回调函数的回调地狱(虽然写起来也挺像的)。不会Promise的可以移步阮一峰的Promise,这里讲的非常清晰。

就现在的发展情况而言,Promise这种解决方案频繁的在我们的代码中出现,当然也成为面试必问的一项,可想而知,理解Promise的实现是非常重要的一点。

本文主要是通过PromiseA+这个规范来一步一步的实现Promise。不了解规范可以看一下下面的规范。

  1. 中文版规范
  2. 英文版规范

从构造函数开始

Promise是一个构造函数,接受一个函数作为参数。从真正的Promise中我们知道,如果接受的不是函数,会报TypeError。

于是我们实现下。

var MyPromise = function(fn){if(typeof fn !== 'function'){throw new TypeError('Promise resolver undefined is not a function');}
}
复制代码

状态

Promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。开始默认状态为pending,只有异步操作的结果才能决定当前是哪一种状态,状态一旦改变,就无法再更改。函数参数接受两个函数作为参数,用于改变状态。我们来实现下。

const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
var MyPromise = function(fn){const _this = this;_this.state = PENDING;_this.value = null;     //Promise 的初始终值_this.resolve = function(value){if(_this.state === PENDING){_this.state = RESOLVED;_this.value =value;   //终值}}_this.reject = function(value){if(_this.state === PENDING){_this.state = REJECTED;_this.value =value; //据因}}
}
复制代码

参数函数自执行

Promise构造函数中的函数参数是自动执行的,并且我们要规避一个问题:当传入的参数抛出异常的情况,如果有直接转成rejected状态。在构造函数中最下面添加代码

try{fn(_this.resolve,_this.reject);
}catch(e){_this.reject(e);
}
复制代码

简单实现then方法

then方法接受两个函数作为参数,同时两个参数都是可选的。如果两个参数都不写的话就形成了透传的作用,留给后面的then方法来接参数。then中的参数方法是在promise状态确认之后,并且在状态未确认之前可能会有多个then方法注册,为了保证resolve函数和reject函数的调用时机需要把构造函数中resolve和reject方法修改成异步,于是我们改下MyPromise构造函数。

//添加两个保存回调函数的数组。
_this.resolveCallbacks = [];    //用于保存then方法中resolve
_this.rejectCallbacks = [];     //用于保存then方法中reject
//修改下resolve和reject方法
_this.resolve = function(value){//判断参数是不是一个promiseif(value instanceof MyPromise){//如果value是一个promise 递归执行   return value.then(_this.resolve,_this.reject);}//异步执行 保证执行顺序setTimeout(() => {if(_this.state === PENDING){_this.state = RESOLVED;_this.value =value;   //终值_this.resolveCallbacks.forEach(cb=>cb(_this.value));}},0)
}
_this.reject = function(value){//异步执行 保证执行顺序setTimeout(()=>{if(_this.state === PENDING){_this.state = REJECTED;_this.value =value; //据因_this.rejectCallbacks.forEach(cb=>(_this.value));}  },)
}
复制代码

我们再来写一下then方法

MyPromise.prototype.then = function(onFulfilled,onRejected){const _self = this;//规范2.2.7.3 /2.2.7.4//如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值//如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因//如果参数不是函数就忽略,同时实现透传  new Promise().then().then(x=>x)onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x;onRejected = typeof onRejected === 'function' ? onRejected : r => {throw r};if(_self.state ===PENDING){_self.resolveCallbacks.push(onFulfilled);_self.rejectCallbacks.push(onRejected);}if(_self.state === RESOLVED){onFulfilled(_self.value);}if(_self.state === REJECTED){onRejected(_self.value);}
}
复制代码

then方法必须返回一个Promise

规范2.27 Promise的then方法返回一个新的promise。我们来修改一下then方法(先把多余的注释去掉,加上新的注释,最后面会留一个完整的)。

MyPromise.prototype.then = function(onFulfilled,onRejected){const _self = this;//规范2.2.7//promise 的then方法返回一个新的promisevar promise2;onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x;onRejected = typeof onRejected === 'function' ? onRejected : r => {throw r};if(_self.state ===PENDING){return new MyPromise(function(resolve,reject){_self.resolveCallbacks.push(onFulfilled);_self.rejectCallbacks.push(onRejected);})}if(_self.state === RESOLVED){return new MyPromise(function(resolve,reject){onFulfilled(_self.value);})}if(_self.state === REJECTED){return new MyPromise(function(resolve,reject){onRejected(_self.value);})}
}
复制代码

then方法异常处理及兼容调用

在promiseA+规范中的第2.2.7条中我们可以看到这样一些东西。

  1. 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)
  2. 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e

解释一下Promise解决过程:解决过程是为了让不同的Promise都可以兼容的使用。比如说jQuery的Promise。

MyPromise.prototype.then = function(onFulfilled,onRejected){const _self = this;var promise2;onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x;onRejected = typeof onRejected === 'function' ? onRejected : r => {throw r};if(_self.state ===PENDING){return new MyPromise(function(resolve,reject){_self.resolveCallbacks.push(function(){//规范2.2.7.2//如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 etry{var x = onFulfilled(_self.value);//规范2.2.7.1resolutionProcedure(promise2,x,resolve,reject);}catch(e){reject(e);}});_self.rejectCallbacks.push(function(){//规范2.2.7.2try{var x = onRejected(_self.value);//规范2.2.7.1resolutionProcedure(promise2,x,resolve,reject);}catch(e){reject(e);}});})}if(_self.state === RESOLVED){return new MyPromise(function(resolve,reject){//规范2.2.4//保证resolve的调用时机setTimeout(function(){//规范2.2.7.2try{var x = onFulfilled(_self.value);//规范2.2.7.1resolutionProcedure(promise2,x,resolve,reject);}catch(e){reject(e);}},0)})}if(_self.state === REJECTED){return new MyPromise(function(resolve,reject){//规范2.2.4//保证了reject的调用时机setTimeout(function(){//规范2.2.7.2try{var x = onRejected(_self.value);//规范2.2.7.1resolutionProcedure(promise2,x,resolve,reject);}catch(e){reject(e);}})})}
}
复制代码

Promise解决过程的实现

规范上解决过程函数参数严格的限制[[Resolve]](promise2, x)。

function resolutionProcedure(promise2,x,resolve,reject){}
复制代码
避免promise2与x指向同一对象

如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise,避免循环引用。

function resolutionProcedure(promise2,x,resolve,reject){if(promise2 === x){return reject(new TypeError('循环引用'));}
}
复制代码
当x是一个Promise的时候

如果x是一个Promise

  1. 如果x处于等待态,promise需保持等待态直至x被拒绝或执行
  2. 如果x处于执行态,用相同的值执行promise
  3. 如果x处于拒绝态,用相同的据因拒绝promise

在resolutionProcedure中添加代码

if(x instanceof MyPromise){//如果x处于等待太,promise需保持等待态直至x被拒绝或执行if(x.state === PENDING){x.then(function(value){//再次调用该函数是为了确认x resolve的参数是什么类型,如果是基本类型就再次resolve传入下一个thenresolutionProcedure(promise2,value,resolve,reject);},reject)}else{//如果x处于执行态,用相同的值执行promise//如果x处于拒绝态,用相同的据因拒绝promisex.then(resolve,reject);}return;
}
复制代码
当x为对象或者函数的时候

根据规范我们可以得出,当resolve和reject成功执行之后就会忽略掉未执行的一方。在resolutionProcedure中加入一下代码

//规范2.3.3
if(x !== null && (typeof x === 'object' || typeof x === 'function')){//规范2.3.3.2//如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promisetry{//规范2.3.3.1//把 x.then 赋值给 thenvar then =x.then;if(typeof then === 'function'){//规范2.3.3.3//如果 then 是函数,将 x 作为函数的作用域 this 调用之。//传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:then.call(x,//规范2.3.3.3.1//如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)y => {if(called){return ;}called = true;resolutionProcedure(promise2,y,resolve,reject);},//规范2.3.3.3.2//如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promiser =>{if(called){return ;}called = true;resolutionProcedure(promise2,r,resolve,reject);});}else{//规范2.3.3.3.4//如果 then 不是函数,以 x 为参数执行 promiseresolve(x);}}catch(e){//规范2.3.3.3.4if(called){return;}called = true;reject(e);}
}else{//规范2.3.4//如果 x 不为对象或者函数,以 x 为参数执行 promiseresolve(x);
}
复制代码

至此所有PromiseA+规范的东西已经全部实现完了,下面贴出全部加上规范注释的代码。

PromiseA+规范实现

var MyPromise = function(fn){//判断构造函数的参数是否为函数if(typeof fn !== 'function'){throw new TypeError('Promise resolver undefined is not a function');}const _this = this;//promise的状态_this.state = PENDING;//promise的值 _this.value = null;//用于保存then中的回调,当状态为pending的时候才会缓存,并且每个实例至多缓存一次。_this.resolveCallbacks = [];_this.rejectCallbacks = [];//resolve的方法_this.resolve = function(value){//判断参数是不是一个promiseif(value instanceof MyPromise){//如果value是一个promise 递归执行   return value.then(_this.resolve,_this.reject);}//异步执行setTimeout(function(){if(_this.state === PENDING){_this.state = RESOLVED;_this.value = value;_this.resolveCallbacks.forEach(cb => cb(_this.value));}},0)}//reject的方法_this.reject = function(value){//异步执行 保证执行顺序setTimeout(function(){if(_this.state === PENDING){_this.state = REJECTED;_this.value = value;_this.rejectCallbacks.forEach(cb => cb(_this.value));}},0)}//用于解决构造函数传入的函数返回出一个异常的情况//new MyPromise(()=> throw new Error('error'))try{fn(_this.resolve,_this.reject);}catch(e){_this.reject(e);}
}MyPromise.prototype.then = function(onFulfilled,onRejected){const _self = this;//规范2.2.7//promise 的then方法返回一个新的promisevar promise2;//规范2.2.7.3 /2.2.7.4//如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值//如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因//如果参数不是函数就忽略,同时实现透传  new Promise().then().then(x=>x)onResolved = typeof onResolved === 'function' ? onResolved : x => x;onRejected = typeof onRejected === 'function' ? onRejected : r => {throw r};if(_self.state === PENDING){return promise2 = new MyPromise(function(resolve,reject){_self.resolveCallbacks.push(function(){//规范2.2.7.2//如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 etry{var x = onFulfilled(_self.value);//规范2.2.7.1resolutionProcedure(promise2,x,resolve,reject);}catch(e){reject(e);}});_self.rejectCallbacks.push(function(){//规范2.2.7.2try{var x = onRejected(_self.value);//规范2.2.7.1resolutionProcedure(promise2,x,resolve,reject);}catch(e){reject(e);}})})}if(_self.state === RESOLVED){return promise2 = new MyPromise(function(resolve,reject){//规范2.2.4//保证resolve的调用时机setTimeout(function(){//规范2.2.7.2try{var x = onFulfilled(_self.value);//规范2.2.7.1resolutionProcedure(promise2,x,resolve,reject);}catch(e){reject(e);}},0)})}if(_self.state === REJECTED){return promise2 = new MyPromise(function(resolve,reject){//规范2.2.4//保证了reject的调用时机setTimeout(function(){//规范2.2.7.2try{var x = onRejected(_self.value);//规范2.2.7.1resolutionProcedure(promise2,x,resolve,reject);}catch(e){reject(e);}})})}
}
//规范2.3
function resolutionProcedure(promise2,x,resolve,reject){//规范2.3.1//如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise,避免循环引用if(promise2 === x){return reject(new TypeError('循环引用'));}//规范2.3.2//如果x为promiseif(x instanceof MyPromise){//如果x处于等待太,promise需保持等待态直至x被拒绝或执行if(x.state === PENDING){x.then(function(value){//再次调用该函数是为了确认x resolve的参数是什么类型,如果是基本类型就再次resolve传入下一个thenresolutionProcedure(promise2,value,resolve,reject);},reject)}else{//如果x处于执行态,用相同的值执行promise//如果x处于拒绝态,用相同的据因拒绝promisex.then(resolve,reject);}return;}//规范2.3.3.3.3//如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用var called = false;//规范2.3.3if(x !== null && (typeof x === 'object' || typeof x === 'function')){//规范2.3.3.2//如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promisetry{//规范2.3.3.1//把 x.then 赋值给 thenvar then =x.then;if(typeof then === 'function'){//规范2.3.3.3//如果 then 是函数,将 x 作为函数的作用域 this 调用之。//传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:then.call(x,//规范2.3.3.3.1//如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)y => {if(called){return ;}called = true;resolutionProcedure(promise2,y,resolve,reject);},//规范2.3.3.3.2//如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promiser =>{if(called){return ;}called = true;resolutionProcedure(promise2,r,resolve,reject);});}else{//规范2.3.3.3.4//如果 then 不是函数,以 x 为参数执行 promiseresolve(x);}}catch(e){//规范2.3.3.3.4if(called){return;}called = true;reject(e);}}else{//规范2.3.4//如果 x 不为对象或者函数,以 x 为参数执行 promiseresolve(x);}
}
复制代码

根据PromiseA+规范实现Promise相关推荐

  1. html5 promise,从HTML5与PromiseA+规范来看事件循环

    写在最前 本次分享一下从HTML5与PromiseA+规范来迅速理解一波事件循环中的microtask 与macrotask. 欢迎关注我的博客,不定期更新中-- JavaScript小众系列开始更新 ...

  2. PromiseA+规范之手写Promise

    前言 1.Promise 的意义? 在javascript的世界中,所有代码都是单线程执行的,由于这个"缺陷",导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行 ...

  3. 前端进阶之PromiseA+规范

    PromiseA+规范文档 术语 promise 是一个有then方法的对象或者是函数,行为遵循本规范 thenable 是一个有then方法的对象或者是函数 value 是promise状态成功时的 ...

  4. PromiseA+规范解读手写MyPromsie

    PromiseA+规范解读&手写MyPromsie Promise 是什么? 有三种状态 then 手写Promise Promise 是什么? promise 对象 订阅异步回调,在处理异步 ...

  5. 实现一个完美符合Promise/A+规范的Promise

    原文在我的博客中:原文地址 如果文章对您有帮助,您的star是对我最好的鼓励- 简要介绍:Promise允许我们通过链式调用的方式来解决"回调地狱"的问题,特别是在异步过程中,通过 ...

  6. 手写一款符合Promise/A+规范的Promise

    手写一款符合Promise/A+规范的Promise 长篇预警!有点长,可以选择性观看.如果对Promise源码不是很清楚,还是推荐从头看,相信你认真从头看到尾,并且去实际操作了,肯定会有收获的.主要 ...

  7. 深入理解Promise并写一个符合Promise a+规范的Promise代码

    深入理解Promise并写一个符合Promise a+规范的Promise代码 关于Promise函数可以参考我写的这篇文章https://www.cnblogs.com/qiaohong/p/770 ...

  8. 手把手带你实现符合Promise/A+规范的Promise

    文章目录 手把手带你实现符合Promise/A+规范的Promise 什么是Promise/A+规范? 一步步实现自定义Promise 构造函数 resolve 与 reject的构建与基础实现 th ...

  9. 方法 手写promise_实现一个符合 Promise/A+规范的 Promise(typescript 版)

    (给前端大全加星标,提升前端技能) 转自:Col0ring juejin.cn/post/6886360224308035598 写在前面 没错,这又是一篇关于手写 Promise 的文章,想必大家已 ...

最新文章

  1. git reset到之前的某一个commit或者恢复之前删除的某一个分支
  2. Windows系统C语言获取文件夹来的所有文件名的方法
  3. DL框架之darknet:深度学习框架darknet的简介、安装、使用方法的详细攻略
  4. GLKVector3参考
  5. 光学定位与追踪技术_贺岁片《疯狂外星人》中徐峥出演外星人?幕后动捕技术了解一下...
  6. php对浮点数小数取整,php除法取整数
  7. 济南市建筑物矢量数据(Shp格式+带高度)
  8. 取消迅雷接管浏览器下载
  9. 主成分分析与因子分析法
  10. gRPC服务发现负载均衡
  11. STM32配合火焰传感器的火灾报警
  12. 测试听力口语软件,上、英语系学姐最全整理的34个英语学习App 针对听力、口语、阅读...
  13. Mac苹果键盘多个按键没响应该如何解决呢
  14. dvm_lock_sample 解析
  15. Vue3生命周期函数的那些事
  16. Uc_client与ucenter通信原理
  17. Ubuntu 壁纸下载
  18. (1)3DMAX之界面认识
  19. 快来,票字版软件电子发票的设置方式(详细流程)
  20. 用python输出pi的近似值_Python-Pi近似

热门文章

  1. Windows和Linux下通用的线程接口
  2. Windows下批处理文件(.bat)的使用
  3. 边缘检测、Hough变换、轮廓提取、种子填充、轮廓跟踪
  4. 【数据库】适用于SQLite的SQL语句(一)
  5. 【POCO】POCO学习总结(三)——交叉编译
  6. Facebook再曝数据丑闻删除应用数据仍会被泄漏
  7. 学习理发去哪里_作为女性,学习运维工程师去哪里好
  8. 怎么在PHP植入音乐,PHP网站插入音乐
  9. 并发执行变成串行_一篇讲透如何理解数据库并发控制(纯干货)
  10. python name_python中__name__的使用