手写一款符合Promise/A+规范的Promise
栏目: JavaScript · 发布时间: 5年前
内容简介:长篇预警!有点长,可以选择性观看。如果对Promise源码不是很清楚,还是推荐从头看,相信你认真从头看到尾,并且去实际操作了,肯定会有收获的。主要是代码部分有点多,不过好多都是重复的,不必担心Promise的一些用法在此不多赘述,本篇主要带领你手写一个Promise源码,学完你就会发现:Promise没有你想象中的那么难实现简单的同步Promise
手写一款符合Promise/A+规范的Promise
长篇预警!有点长,可以选择性观看。如果对Promise源码不是很清楚,还是推荐从头看,相信你认真从头看到尾,并且去实际操作了,肯定会有收获的。主要是代码部分有点多,不过好多都是重复的,不必担心
Promise的一些用法在此不多赘述,本篇主要带领你手写一个Promise源码,学完你就会发现:Promise没有你想象中的那么难
本篇大概分为以下步骤
- 实现简单的同步Promise
- 增加异步功能
- 增加链式调用then
- 增加catch finally方法
- 增加all race 等方法
- 实现一个promise的延迟对象defer
- 最终测试
实现简单的同步Promise
先大概说一下基本概念:
Promise内部维护着三种状态,即pending,resolved和rejected。初始状态是pending,状态可以有pending--->relolved,或者pending--->rejected.不能从resolve转换为rejected 或者从rejected转换成resolved.
即 只要Promise由pending状态转换为其他状态后,状态就不可变更。
ok.知道了这些后,我们开始手撸代码:
注意观看序号 1 2 3 4 5 ...
function Promise(executor){ let that = this; /** 2 定义初始的一些变量 */ that.status = 'pending'; that.value = null; that.reason = null; /** 3 定义初始的成功和失败函数 */ function resolve(value){ /** 4 判断状态是不是初始状态pending * 是就转换状态 否则不转换 * 确保状态的变化后的不可变性 */ if(that.status === 'pending'){ that.status = 'resolved'; that.value = value; } } function reject(reason){ /** 5 同上 */ if(that.status === 'pending'){ that.status = 'rejected'; that.reason = reason; } } /** * 1 Promise中首先传了一个executor,它是一个函数 * executor函数中又传了两个函数,分别是resolve和reject * 很显然 resolve是成功回调,reject是失败的回调 */ executor(resolve,reject); } /** 6 在Promise原型上面定义then方法 * then方法上面有两个回调 一个是成功后的方法 另一个是失败后的方法 * 根据成功或失败的状态去执行相关成功onFilfulled()或者失败onRejected()的回调方法 */ Promise.prototype.then = function(onFilfulled,onRejected){ let that = this; if(that.status === 'resolved'){ /** 7 如果状态已经变更为resolved * 说明resolve方法已经被调用 * 那么此时就执行成功的回调函数onFilfulled * 并传入参数 that.value * */ onFilfulled(that.value); } if(that.status === 'rejected'){ /** 8 同上 * 传入参数 that.reason */ onRejected(that.reason); } } module.exports = Promise;
通过require()引入手撸的Promise
let Promise = require('./myPromise'); let p1 = ()=>{ return new Promise((resolve,reject)=>{ resolve('success.1'); }); } p1().then((data)=>{ console.log(data); // 打印 success.1 },(err)=>{ console.log(err); });
ok.经调用发现 此代码可以实现部分Promise的功能,但仅仅是同步下才有效果。
那异步呢? 别急这就来~:
增加异步功能
注意观看序号 1 2 3 4 5 ...
function Promise(executor){ let that = this; that.status = 'pending'; that.value = null; that.reason = null; /** 1 因为异步不是立即执行 状态不会变更 成功或失败的回调函数也不会执行 * 所以先定义好存放成功或失败回调函数的数组 * 以便将成功或失败的回调函数先保存起来 * */ that.onFilFulledCallbacks = []; that.onRejectedCallbacks = []; function resolve(value){ if(that.status === 'pending'){ that.status = 'resolved'; that.value = value; /** 3 发布 * 等待状态发生变更 * 状态变更后 立即执行之前存放在相应数组中所有的成功或失败的回调函数 * 即 发布 */ that.onFilFulledCallbacks.forEach((fn)=>{ fn(); }); } } function reject(reason){ if(that.status === 'pending'){ that.status = 'rejected'; that.reason = reason; /** 4 同上 */ that.onRejectedCallbacks.forEach((fn)=>{ fn(); }); } } executor(resolve,reject); } Promise.prototype.then = function(onFilfulled,onRejected){ let that = this; if(that.status === 'resolved'){ onFilfulled(that.value); } if(that.status === 'rejected'){ onRejected(that.reason); } /** 2 订阅 * 因为是异步 状态当时并没有立即变更 所以状态还是pending * 此时需要把成功或者失败的回调函数存放到对应的数组中 * 等待状态变更时 再从数组中拿出来去执行 * 即 订阅 * *存放数组时 为了执行时方便 需要把回调函数的外层包裹一层空函数 */ if(that.status === 'pending'){ that.onFilFulledCallbacks.push(function(){ onFilfulled(that.value); }); } if(that.status === 'pending'){ that.onRejectedCallbacks.push(function(){ onRejected(that.reason); }); } } module.exports = Promise;
代码测试:
let Promise = require('./myPromise'); let p1 = ()=>{ return new Promise((resolve,reject)=>{ setTimeout(function(){ resolve('success.1'); // reject('fail.'); },1500); }); } p1().then((data)=>{ console.log(data); // success.1 },(err)=>{ console.log(err); });
可以看到 1.5s后 执行了resolve() 并打印了success.1,至此,我们实现了异步的Promise.其实这里的实现异步的思想就是发布订阅.
en~ok.高能预警:tiger:.接下来就稍稍复杂了 因为我们要实现链式调用then。 要实现这个功能那我们就要重写then方法,并在then方法中重新返回一个Promise,只有这样,才可以实现多次调用then.而且要新增一个解析返回值是否为promise的函数.
稍微捋下逻辑:
- 如果一个then方法返回一个普通值的话,这个值会传递给下一个then中作为resolve成功的结果
- 如果一个then方法返回一个promise的话,会根据返回的promise是成功还是失败,决定下一个then是成功还是失败
:ok_hand: 上代码:
增加链式调用then
注意观看序号 1 2 3 4 5 ...
function Promise(executor){ let that = this; that.status = 'pending'; that.value = null; that.reason = null; that.onFilFulledCallbacks = []; that.onRejectedCallbacks = []; function resolve(value){ if(that.status === 'pending'){ that.status = 'resolved'; that.value = value; that.onFilFulledCallbacks.forEach((fn)=>{ fn(); }); } } function reject(reason){ if(that.status === 'pending'){ that.status = 'rejected'; that.reason = reason; that.onRejectedCallbacks.forEach((fn)=>{ fn(); }); } } executor(resolve,reject); } Promise.prototype.then = function(onFilfulled,onRejected){ let that = this; /** 1 让promise2等于一个新的Promise 并将promise2返回 */ let promise2 = new Promise((resolve,reject)=>{ if(that.status === 'resolved'){ /** 2 因为返回了promise2 * 并且第3步resolvePromiseRelation函数中传递了promise2 * 而目前promise2并没有拿到 * 所以加一个定时器 异步执行 等到promise2拿到后 * 再去执行 resolvePromiseRelation()方法 并将promise2传递进去*/ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); /** 3 判断新返回值是什么类型的函数 * 并将当前的promise:promise2 新的返回值:promise3 * 和 成功时回调:esolve 失败时回调:reject 作为参数传进去 */ resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'rejected'){ /** 同2 */ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); /** 同3*/ resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'pending'){ that.onFilFulledCallbacks.push(function(){ /** 同2 */ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); /** 同3*/ resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); } if(that.status === 'pending'){ that.onRejectedCallbacks.push(function(){ /** 同2 */ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); /** 同3*/ resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); } }); /** 同1 */ return promise2; } function resolvePromiseRelation(promise2,promise3,resolve,reject){ /** 4 防止自己等待自己 一直循环等待 */ if(promise2 === promise3){ return reject(new TypeError('循环引用了!')); } /** 8 一个标示 表示当前没有被调用过 * 确保resolve或者reject后的状态不会再次发生变更 */ let called; /** 5 保证promise3是一个引用类型 * 判断新返回值promise3的类型 * 如果是普通值常量 就直接resolve导出 */ if(promise3!==null&&(typeof promise3 === 'object'||typeof promise3 === 'function')){ try{ /** 6 确保promise3是一个Promise * 判断promise3的then方法 * 如果存在 并且是一个function类型 * 就表示promise3是一个Promise */ if(typeof promise3.then === 'function'){ /** 9 执行promise3的then方法 * 因为promise3也是一个Promise * 需要再次解析promise3的then方法 * 直到解析到最后的返回值不是一个Promise类型为止 */ promise3.then(promise3, (promise4)=>{ /** 同8 */ if(called) return; called = true; /** 10 递归解析新的返回值的类型 * 解析到返回值不是一个Promise类型为止 */ resolvePromiseRelation(promise3,promise4,resolve,reject); },(r)=>{ /** 同8 */ if(called) return; called = true; reject(r); }); }else{ /** 7 此时promise3是一个普通对象 直接resolve() */ resolve(promise3); } }catch(e){ /** 同8 */ if(called) return; called = true; reject(e); }; }else{ /** 同5 普通值直接resolve()*/ resolve(promise3); } } module.exports = Promise;
ok. 至此 我们已经实现了Promsie的异步和链式调用. Promise中比较复杂的部分我们已经搞定了 接下来就是添加一些方法,其实这部分反而没那么复杂了.
catch : catch方法本质上就是一个then方法的变形,只有失败时的回调 没有成功时的回调
finally : finally方法的作用是不管 Promise 对象最后状态如何,都会执行操作.其实说白了就是在then方法的成功和失败的回调函数中都执行该方法就行了.
ok.老规矩 上代码~
增加catch finally方法
function Promise(executor){ let that = this; that.status = 'pending'; that.value = null; that.reason = null; that.onFilFulledCallbacks = []; that.onRejectedCallbacks = []; function resolve(value){ if(that.status === 'pending'){ that.status = 'resolved'; that.value = value; that.onFilFulledCallbacks.forEach((fn)=>{ fn(); }); } } function reject(reason){ if(that.status === 'pending'){ that.status = 'rejected'; that.reason = reason; that.onRejectedCallbacks.forEach((fn)=>{ fn(); }); } } executor(resolve,reject); } Promise.prototype.then = function(onFilfulled,onRejected){ /** 2 此处有个坑 如果只写1 不写2的话 * 会报一个TypeError :onRejected is not a function * 在此处给它一个默认的成功和失败的回调函数就好 */ onFilfulled = typeof onFilfulled === 'function'?onFilfulled:value=>value; onRejected = typeof onRejected === 'function'?onRejected:err=>{throw err}; let that = this; let promise2 = new Promise((resolve,reject)=>{ if(that.status === 'resolved'){ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'rejected'){ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'pending'){ that.onFilFulledCallbacks.push(function(){ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); } if(that.status === 'pending'){ that.onRejectedCallbacks.push(function(){ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); } }); return promise2; } function resolvePromiseRelation(promise2,promise3,resolve,reject){ if(promise2 === promise3){ return reject(new TypeError('循环引用了!')); } let called; if(promise3!==null&&(typeof promise3 === 'object'||typeof promise3 === 'function')){ try{ if(typeof promise3.then === 'function'){ promise3.then(promise3, (promise4)=>{ if(called) return; called = true; resolvePromiseRelation(promise3,promise4,resolve,reject); },(r)=>{ if(called) return; called = true; reject(r); }); }else{ resolve(promise3); } }catch(e){ if(called) return; called = true; reject(e); }; }else{ resolve(promise3); } } /** 1 直接返回this的then方法 * 因为catch只捕获错误 所以resolve直接为null * 返回reject就好*/ Promise.prototype.catch = function(errFn){ return this.then(null,errFn); } /** 3 finally实现起来也很简单 * 分别在resolve和reject中执行fn就好 * 最后再把this返回出去就好 */ Promise.prototype.finally = function(fn){ this.then(()=>{ fn(); },()=>{ fn(); }); return this; } module.exports = Promise;
增加all race 等方法
function Promise(executor){ let that = this; that.status = 'pending'; that.value = null; that.reason = null; that.onFilFulledCallbacks = []; that.onRejectedCallbacks = []; function resolve(value){ if(that.status === 'pending'){ that.status = 'resolved'; that.value = value; that.onFilFulledCallbacks.forEach((fn)=>{ fn(); }); } } function reject(reason){ if(that.status === 'pending'){ that.status = 'rejected'; that.reason = reason; that.onRejectedCallbacks.forEach((fn)=>{ fn(); }); } } executor(resolve,reject); } Promise.prototype.then = function(onFilfulled,onRejected){ onFilfulled = typeof onFilfulled === 'function'?onFilfulled:value=>value; onRejected = typeof onRejected === 'function'?onRejected:err=>{throw err}; let that = this; let promise2 = new Promise((resolve,reject)=>{ if(that.status === 'resolved'){ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'rejected'){ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'pending'){ that.onFilFulledCallbacks.push(function(){ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); } if(that.status === 'pending'){ that.onRejectedCallbacks.push(function(){ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); } }); return promise2; } function resolvePromiseRelation(promise2,promise3,resolve,reject){ if(promise2 === promise3){ return reject(new TypeError('循环引用了!')); } let called; if(promise3!==null&&(typeof promise3 === 'object'||typeof promise3 === 'function')){ try{ if(typeof promise3.then === 'function'){ promise3.then(promise3, (promise4)=>{ if(called) return; called = true; resolvePromiseRelation(promise3,promise4,resolve,reject); },(r)=>{ if(called) return; called = true; reject(r); }); }else{ resolve(promise3); } }catch(e){ if(called) return; called = true; reject(e); }; }else{ resolve(promise3); } } Promise.prototype.catch = function(errFn){ return this.then(null,errFn); } Promise.prototype.finally = function(fn){ this.then(()=>{ fn(); },()=>{ fn(); }); return this; } /** 1 直接在构造函数上增加all方法 * 它返回的也是一个Promise * 等待参数数组中所有的promise都执行完毕后 * 再返回结果 */ Promise.all = function(values){ return new Promise((resolve,reject)=>{ /** 2 定义一个存放最终结果的数组result和一个index */ let results = []; let index = 0; /** 3 定义一个方法addToArr() * 让index每次执行增加results数组元素的函数的时候都+1 * 当index === values的长度的时候 说明此时所有promsie都执行完毕并放到的数组中 * 然后直接resolve(results)就行了 */ function addToArr(key,value){ index++; results[key] = value; /** 6 当满足条件时 说明所有的promise都执行完毕 直接resolve(results) */ if(index === values.length){ resolve(results); } } /** 4 循环values中的每一项promsie */ for(let i = 0; i < values.length; i++){ let current = values[i]; /** 5 判断每一项promise的返回值是不是一个Promsie * 是的话就执行该Promise的then方法 拿到返回值 并放到数组results中 * 是一个普通值的话就直接将该值放到数组results中 */ if(current && current.then && typeof current.then === 'function'){ current.then((value)=>{ /** 同5 把返回值放到数组results中*/ addToArr(i,value); },reject); }else{ /** 同5 把返回值放到数组results中*/ addToArr(i,current); } } }); } /** race方法相比较于all方法简单很多 * 因为race中的promsie成功resolve一个 * 整个race就resolve */ Promise.race = function(values){ return new Promise((resolve,reject)=>{ /** 同4 */ for(let i = 0; i < values.length; i++){ let current = values[i]; /** 同5 */ if(current&¤t.then&&typeof current.then === 'function'){ /** 7 直接执行then就好 */ current.then(resolve,reject); }else{ /** 8 普通值直接resolve */ resolve(current); } } }); } module.exports = Promise;
实现一个promise的延迟对象defer
此步是为了测试我们手写的Promsie符不符合Promsie/A+规范,如果没有defer的话,我们在测试过程中就会报一个TypeError: adapter.deferred is not a function.
其实写完defer后,我们就可以去进行测试我们手写的Promsie符不符合Promsie/A+规范了。
即:本篇手写一款符合Promise/A+规范的Promise的最终本为:
function Promise(executor){ let that = this; that.status = 'pending'; that.value = null; that.reason = null; that.onFilFulledCallbacks = []; that.onRejectedCallbacks = []; function resolve(value){ if(that.status === 'pending'){ that.status = 'resolved'; that.value = value; that.onFilFulledCallbacks.forEach((fn)=>{ fn(); }); } } function reject(reason){ if(that.status === 'pending'){ that.status = 'rejected'; that.reason = reason; that.onRejectedCallbacks.forEach((fn)=>{ fn(); }); } } executor(resolve,reject); } Promise.prototype.then = function(onFilfulled,onRejected){ onFilfulled = typeof onFilfulled === 'function'?onFilfulled:value=>value; onRejected = typeof onRejected === 'function'?onRejected:err=>{throw err}; let that = this; let promise2 = new Promise((resolve,reject)=>{ if(that.status === 'resolved'){ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'rejected'){ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); } if(that.status === 'pending'){ that.onFilFulledCallbacks.push(function(){ setTimeout(()=>{ try{ let promise3 = onFilfulled(that.value); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); that.onRejectedCallbacks.push(function(){ setTimeout(()=>{ try{ let promise3 = onRejected(that.reason); resolvePromiseRelation(promise2,promise3,resolve,reject); }catch(e){ reject(e); } },0); }); } }); return promise2; } function resolvePromiseRelation(promise2,promise3,resolve,reject){ if(promise2 == promise3){ return reject(new TypeError('循环引用了!')); } let called; if(promise3!==null&&(typeof promise3 === 'object' || typeof promise3 === 'function')){ try{ if(typeof promise3.then === 'function'){ promise3.then.call(promise3, (promise4)=>{ if(called) return; called = true; resolvePromiseRelation(promise3,promise4,resolve,reject); },(r)=>{ if(called) return; called = true; reject(r); }); }else{ resolve(promise3); } }catch(e){ if(called) return; called = true; reject(e); }; }else{ resolve(promise3); } } Promise.prototype.catch = function(errFn){ return this.then(null,errFn); } Promise.prototype.finally = function(fn){ this.then(()=>{ fn(); },()=>{ fn(); }); return this; } Promise.all = function(values){ return new Promise((resolve,reject)=>{ let results = []; let index = 0; function addToArr(key,value){ index++; results[key] = value; if(index === values.length){ resolve(results); } } for(let i = 0; i < values.length; i++){ let current = values[i]; if(current && current.then && typeof current.then === 'function'){ current.then((value)=>{ addToArr(i,value); },reject); }else{ addToArr(i,current); } } }); } Promise.race = function(values){ return new Promise((resolve,reject)=>{ for(let i = 0; i < values.length; i++){ let current = values[i]; if(current&¤t.then&&typeof current.then === 'function'){ current.then(resolve,reject); }else{ resolve(current); } } }); } // 实现一个promise的延迟对象 defer Promise.defer = Promise.deferred = function(){ let dfd = {}; dfd.promise = new Promise((resolve, reject)=>{ dfd.resolve = resolve; dfd.reject = reject; }); return dfd; } module.exports = Promise;
最终测试
- 测试当前代码是否符合Promise/A+规范
- 全局安装 npm i -g promises-aplus-tests
- 文件所在目录运行以下命令 (例如你的文件名为:MyPrommise.js)
- promise-aplus-tests MyPrommise.js
- 等待
-
ok.
源码在github上,写文章不易,欢迎star或fork,thx~
github
以上所述就是小编给大家介绍的《手写一款符合Promise/A+规范的Promise》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 只会用就out了,手写一个符合规范的Promise
- 从手写一个符合Promise/A+规范Promise来深入学习Promise
- 如何判断URL格式是否符合规范?
- [译] 打造符合用户期望的应用质量
- 符合语言习惯的 Python 优雅编程技巧
- 实现一个符合Promise/A+规范的Promise
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。