我们手写一个Promise/A+规范,然后安装测试脚本,以求通过这个规范。

//Promise/A+源代码
// new Promise时,需要传递一个executor执行器,执行器立即执行
// executor接受两个参数,分别是resolve和reject
// promise只能从pending到rejected,或者从pending到fulfilled
// promise的状态一旦确认,就不会再改变
// promise有then方法,then接受两个参数,分别是promise成功的回调onFulfilled
// 和promise失败的回调 onRejected
// 如果调用then时,promise已经成功,则执行onFulfilled,并将promise值作为参数传递进去
// 如果promise已经失败,那么执行onRejected并把promise失败的原因作为参数传递进去
// 如果promise状态是pending,需要将onFulfilled和onRejected函数存放起来,等待状态确定后,再一次将对应的函数执行(发布)
// then的参数onFulfilled和onRejected可以缺省
// promise可以then多次,promise的then方法返回一个promise
// 如果then返回的是一个结果,那么就把这个结果作为参数,传递给下一个then的成功的回调onFulfilled
// 如果then中抛出了异常,那么就会把这个异常作为参数,传递给下一个失败的回调 onRejected
// 如果then返回的是一个promise,那么需要等这个promise,那么会等这个promise执行完,promise如果成功
// 就走下一个then的成功,如果失败,就走下一个then的失败
//pending状态,可以认为是一种中间状态
const PENDING = 'pending';
// fullfilled完成状态,可以认为是成功回调了的状态
const FULFILLED = 'fullfilled';
// reject即为拒绝状态,即失败的状态
const REJECTED = 'reject';
// executor是一个执行器
function Promise(executor){
let self = this;
self.status = PENDING;
self.onFulfilled = [];//成功的回调
self.onRejected = [];//失败的回调
// PromiseA+ 2.1
function resolve(value){
if(self.status === PENDING){
self.status = FULFILLED;
self.value = value;
self.onFulfilled.forEach(fn=>fn());//PromiseA+ 2.2.6.1
}
}
function reject(reason){
if(self.status===PENDING){
self.status = REJECTED;
self.reason = reason;
self.onRejected.forEach(fn=>fn());//PromiseA+ 2.2.6.2
}
}
try{
executor(resolve,reject)
}catch(e){
reject(e)
}
}
// then必须返回一个promise
Promise.prototype.then = function(onFulfilled,onRejected){
// PromiseA+ 2.2.1/PromiseA+ 2.2.5/PromiseA+ 2.2.7.3/PromiseA+2.2.7.4
onFulfilled = typeof onFulfilled ==='function'?onFulfilled:value =>value;
onRejected = typeof onRejected === 'function'?onRejected:reason=>{throw reason};
let self = this;
//PromiseA+ 2.2.7
let Promise2 = new Promise((resolve,reject)=>{
if(self.status===FULFILLED){
//PromiseA+2.2.2
// Promise A+2.2.4----setTimeout
setTimeout(()=>{
try{
let x = onFulfilled(self.value);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
//PromiseA+2.2.7.2
reject(e)
}
});
}else if(self.status === REJECTED){
// PromiseA+ 2.2.3
setTimeout(()=>{
try{
let x = onRejected(self.reason);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
})
}else if(self.status===PENDING){
self.onFulfilled.push(()=>{
setTimeout(()=>{
try{
let x = onFulfilled(self.value);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
});
});
}
});
return promise2;
}
function resolvePromise(promise2,x,resolve,reject){
let self = this;
//PromiseA+ 2.3.1
if(promise2===x){
reject(new TypeError('changing cycle'));
}
if(x && typeof x ==="object"|| typeof x ==='function'){
let used;//PromiseA+ 2.3.3.3.3//只能调用一次
try{
let then = x.then;
if(typeof then === 'function'){
//PromiseA+2.3.3
then.call(x,(y)=>{
// PromiseA+ 2.3.3.1
if(used)return;
used = true;
resolvePromise(promise2,y,resolve,reject);
},(r)=>{
//PromiseA+2.3.3.2
if(used)return;
used = true;
reject(r)
});
}else{
//PromiseA+ 2.3.3.4
if(used)return;
used = true;
reject(x);
}
}catch(e){
//PromiseA+ 2.3.3.2
if(used)return;
used = true;
reject(e);
}
}else{
//PromiseA+ 2.3.3.4
resolve(x);
}
}
module.exports = Promise;

我们可以用专门的测试脚本测试所编写的代码是否符合PromiseA+的规范。

在上面的promise实现的代码中,增加一下代码

Promise.defer = Promise.deferred = function(){
let dfd = {};
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}

安装测试脚本

npm install -g promises-aplus-tests

如果当前的promise源码的文件名为promise.js

那么在对应的目录执行以下命令:

promises-aplus-tests promise.js

promises-aplus-tests中共有872条测试用例。以上代码,可以完美通过所有用例。

本文借鉴自:https://blog.csdn.net/liuyan19891230/article/details/88385973

这篇博客是ES6的写法:https://blog.csdn.net/weixin_41436338/article/details/79806033

有空就多学多看源码,你看那么多人那么牛逼啊

Promise的源码实现(符合Promise/A+规范)的更多相关文章

  1. Promise的源码实现(完美符合Promise/A+规范)

    Promise是前端面试中的高频问题,我作为面试官的时候,问Promise的概率超过90%,据我所知,大多数公司,都会问一些关于Promise的问题.如果你能根据PromiseA+的规范,写出符合规范 ...

  2. JS魔法堂: Native Promise Only源码剖析

    一, 前言 深入学习Promise的朋友应该都看过<深入理解Promise五部曲>这一系列的文章, 以解除回调地狱之外的观点来剖析Promise更多的内涵,确实十分精彩. Part 1: ...

  3. promise/bluebird源码

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/bluebirdsource 本博客同步在http://www.cnb ...

  4. 异步解决方案promise及源码实现

    js语言的特性,造就了特别的异步处理方式,我记得以前用的最多的就是回调函数,那个时候写jquery的ajax时候,特别喜欢写这种代码: $.ajax({ method:'get', url:" ...

  5. Django day24 cbv和APIView的源码分析 和 resful的规范

    一:cbv的源码分析 1.CBV和FBV的区别: - Class Base View   CBV(基于类的视图) - Function Base View   FBV(基于函数的视图) 2.as_vi ...

  6. [编织消息框架][netty源码分析]9 Promise 实现类DefaultPromise职责与实现

    netty Future是基于jdk Future扩展,以监听完成任务触发执行Promise是对Future修改任务数据DefaultPromise是重要的模板类,其它不同类型实现基本是一层简单的包装 ...

  7. jQuery之Deferred源码剖析

    一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...

  8. jquery源码解析:jQuery延迟对象Deferred(工具方法)详解2

    请接着上一课继续看. $.Deferred()方法中,有两个对象,一个是deferred对象,一个是promise对象. promise对象有以下几个方法:state,always,then,prom ...

  9. promise源码解析

    前言 大部分同学对promise,可能还停留在会使用es6的promise,还没有深入学习.我们都知道promise内部通过reslove.reject来判断执行哪个函数,原型上面的then同样的,也 ...

随机推荐

  1. 次短路 /// dijkstra oj1597

    题目大意: 给出一个有向图,求从 顶点a 到 顶点b 的次短路. 第一行是2个正整数 n 和 e,表示该有向图的顶点数和边数.3 < n ≤ 5000 , 3 < e < 40000 ...

  2. AbstractByteBuf 源码分析

    主要成员变量 static final ResourceLeakDetector<ByteBuf> leakDetector = new ResourceLeakDetector<B ...

  3. ant的build.xml备份

    <?xml version="1.0" encoding="UTF-8" ?> <project default="rerun&qu ...

  4. 小米手机 DELETE_FAILED_INTERNAL_ERROR Error while Installing APKs

    手机:小米2s,MIUI 9 7.11.16 开发版 手机已处于开发者模式,启用了USB调试,已使用USB线连接了手机,在Android Studio 工具栏点击 "Run ‘app’(Sh ...

  5. vue+ivew使用Collapse 折叠面板把全部面板展开

    1.需求: 在使用搜索功能时候,只显示搜索到的panel并且将搜索到的含有该专家的panel展开,如图                1.html,注意黄色部分,作为每个panel的key值,要唯一 ...

  6. duilib教程之duilib入门简明教程3.第一个程序 Hello World

    小伙伴们有点迫不及待了么,来看一看Hello World吧:新建一个空的win32项目,新建一个main.cpp文件,将以下代码复制进去: #include <windows.h> #in ...

  7. 第九章 Odoo 12开发之外部 API - 集成第三方系统

    Odoo 服务器端带有外部 API,可供网页客户端和其它客户端应用使用.本文中我们将学习如何在我们的客户端程序中使用 Odoo 的外部 API.为避免引入大家所不熟悉的编程语言,此处我们将使用基于 P ...

  8. kafka数据分区的四种策略

    kafka的数据的分区 探究的是kafka的数据生产出来之后究竟落到了哪一个分区里面去了 第一种分区策略:给定了分区号,直接将数据发送到指定的分区里面去 第二种分区策略:没有给定分区号,给定数据的ke ...

  9. NOIP 2017 提高组 day1t2 时间复杂度

    P3952 时间复杂度 标签 NOIp提高组 2017 时空限制 1000ms / 128MB 小明正在学习一种新的编程语言 A++,刚学会循环语句的他激动地写了好多程序并 给出了他自己算出的时间复杂 ...

  10. mybatis接口映射

    通过sqlSession.getMapper();方法获取映射的接口及方法 sqlSession调用Configuration的getMapper方法,方法中使用了mapperRegistry.get ...