手写一款符合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.高能预警

手写一款符合Promise/A+规范的Promise的更多相关文章

  1. 一起学习造轮子(一):从零开始写一个符合Promises/A+规范的promise

    本文是一起学习造轮子系列的第一篇,本篇我们将从零开始写一个符合Promises/A+规范的promise,本系列文章将会选取一些前端比较经典的轮子进行源码分析,并且从零开始逐步实现,本系列将会学习Pr ...

  2. 如何手写一款KOA的中间件来实现断点续传

    本文实现的断点续传只是我对断点续传的一个理解.其中有很多不完善的地方,仅仅是记录了一个我对断点续传一个实现过程.大家应该也会发现我用的都是一些H5的api,老得浏览器不会支持,以及我并未将跨域考虑入内 ...

  3. 一步一步实现一个Promise A+规范的 Promise

    2015年6月,ES2015(即ES6)正式发布后受到了非常多的关注.其中很重要的一点是 Promise 被列为了正式规范. 在此之前很多库都对异步编程/回调地狱实现了类 Promise 的应对方案, ...

  4. 如何手写一款SQL injection tool?

    0×01 前言 我想在FreeBuf上出没的人一般都是安全行业的,或者说是安全方面的爱好者,所以大家对sql注入应该都比较了解,反正我刚入门的时候就是学的这些:sql注入.xss之类的.sql注入从出 ...

  5. [手写系列] 带你实现一个简单的Promise

    简介 学习之前 需要先对Promise有个基本了解哦,这里都默认大家都是比较熟悉Promise的 本次将带小伙伴们实现Promise的基本功能 Promise的基本骨架 Promise的then Pr ...

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

    const PENDING = "pending"; const RESOLVED = "resolved"; const REJECTED = "r ...

  7. 手写基于Promise A+规范的Promise

    const PENDING = 'pending';//初始态const FULFILLED = 'fulfilled';//初始态const REJECTED = 'rejected';//初始态f ...

  8. 参考KOA,5步手写一款粗糙的web框架

    我经常在网上看到类似于KOA VS express的文章,大家都在讨论哪一个好,哪一个更好.作为小白,我真心看不出他两who更胜一筹.我只知道,我只会跟着官方文档的start做一个DEMO,然后我就会 ...

  9. 手写一个Promise/A+,完美通过官方872个测试用例

    前段时间我用两篇文章深入讲解了异步的概念和Event Loop的底层原理,然后还讲了一种自己实现异步的发布订阅模式: setTimeout和setImmediate到底谁先执行,本文让你彻底理解Eve ...

随机推荐

  1. Struts2使用struts标签判断变量是否为空的写法

    <%@taglib uri="/struts-tags" prefix="s"%> <span id="viewOrgName&qu ...

  2. zookeeper两台内网服务器彼此调不到服务的问题。

    Start NettyClient /172.20.11.52 connect to the server /172.20.11.52:20881, dubbo version: 2.5.3, cur ...

  3. Mongodb系列文章

    http://blog.csdn.net/congcong68 学习MongoDB 六: MongoDB查询(游标操作.游标信息)(三) Mongodb官方C#驱动示例 MongoDB Driver ...

  4. Echarts树图定制详解

    本文讲的是如何定制Echarts的tree图.主要包括下载.全局变量名修改.左键菜单添加.右键菜单添加.内容缩放.文本过滤高亮等. 一 说明 Echarts中提供了tree图,但实际项目中,该tree ...

  5. SpringCloud 进阶之Ribbon和Feign(负载均衡)

    1. Ribbon 负载均衡 Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端,负载均衡的工具; 1.1 Ribbon 配置初步 1.1.1 修改 micros ...

  6. Django 框架搭建入门案例

    1. 什么是 web 框架 对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端; # 示例: import socket def handle_re ...

  7. spring 拾遗

    1.@PostConstruct VS  init-method 1.1 both BeanPostProcessor 1.2 @PostConstruct is a JSR-250 annotati ...

  8. java 多线程 day09 线程池

    import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.c ...

  9. JS通过身份证号获取生日、年龄、性别

    <script> function IdCard(UUserCard,num){ if(num==1){ //获取出生日期 birth=UUserCard.substring(6, 10) ...

  10. CF519 ABCD D. A and B and Interesting Substrings(map,好题)

    A:http://codeforces.com/problemset/problem/519/A 水题没什么好说的. #include <iostream> #include <st ...