我们手写一个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. 关于priority_queue和sort()对结构体数组的排序

    知乎的这个答案很清晰https://www.zhihu.com/question/35736022 #include <iostream> #include <algorithm&g ...

  2. 滑雪 矩阵中的最长上升路径 /// 记忆化DFS || DP oj22919

    大致题意: Description 难怪Michael喜欢滑雪,因为滑雪确实很刺激.为了获得加速度,滑雪道必须向下倾斜,而且当滑到坡底,你不得不再次走上坡或者等待升降机来载你.Michael想知道在一 ...

  3. HTML中使用js的三种方式及优缺点介绍

    1.内部js: 在直接在页面的<script></script>标签内写js代码 优点:相对于使用行内js,内部js代码较为集中,与页面结构的实现代码耦合度较低,比较便于维护 ...

  4. angular 页签

    HTML: <link rel="stylesheet" href="views/show/tab.css"/> <div> <u ...

  5. pycharm IDE在导入自定义模块时提示有错,但实际没错

    在建立python项目时,有时为了区分资源和代码,如在项目文件夹下新建img和src两个文件夹,这时导入自定义模块会提示错误,结果没错但感觉别扭.如: 这是因为pycharm提示功能是从根目录上去寻找 ...

  6. ie中onclick问题

    代码:<button > <span onclick="xxx();">确定</span></button> 在chrome和fir ...

  7. day2-元组、字典、文件操作

    学习内容: 1. 元组操作 2. 字典操作 3. 文件操作 4. 深浅copy 1. 元组操作: 元组和列表非常相似,只不过元组不能在原处修改(它是不可变的),并且通常写成圆括号中的一系列项. # 元 ...

  8. day1-字符串、列表

    字符串操作: name = "Wills Qian" # 创建字符串变量 print(len(name)) # 打印字符串长度 print(name[0]) # 提取第一个字符W ...

  9. 廖雪峰Java13网络编程-3其他-2RMI远程调用

    1.RMI远程调用: Remote Method Invocation 目的:把一个接口方法暴露给远程 示例: 定义一个接口Clock,它有一个方法能够获取当前的时间,并编写一个实现类,来实现这个接口 ...

  10. 【转载】gdb基本命令总结

    本文介绍使用gdb调试程序的常用命令. 主要内容: [简介] [举例] [其他] [简介]  ============= GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具.如果你是在 U ...