概述

异步编程离不开promise, async, 事件响应这些东西,为了更好地异步编程,我打算探究一下promise的实现原理,方法是自己实现一个简易的promise。

根据promise mdn上的描述,我们主要实现如下api:

  1. Promise.prototype.resolve
  2. Promise.prototype.reject
  3. Promise.prototype.then
  4. Promise.all
  5. Promise.race

为了更好地性能和使用,我还需要加上惰性求值特性,即:只有调用then的时候才真正调用Promise里面的异步方法。并且我们忽略pending,fulfilled和rejected这几个状态,因为封装后的promise并没有暴露这几个状态,目前看来也没什么用(除非用事件响应实现Promise)。

为了简便,暂时不考虑错误处理

实现resolve,reject和then

其实就是调用resolve和reject的时候调用相应的success和error函数就行,代码如下:

let Promise = function(func) {
this.func = func;
this.success = null;
this.error = null;
} Promise.prototype.resolve = function(value, that) {
console.log('resolve');
if(typeof that.success == 'function') {
that.success(value);
}
} Promise.prototype.reject = function(value, that) {
console.log('reject');
if(typeof that.error == 'function') {
that.error(value);
}
} Promise.prototype.then = function(onFulfilled, onRejected) {
this.success = onFulfilled;
this.error = onRejected;
setTimeout(() => {
this.func(this.resolve, this.reject);
});
} let myPromise = new Promise(function(resolve, reject){
setTimeout(() => {
resolve("成功!", this);
}, 1000);
}); myPromise.then((successMessage) => {
console.log('输出', successMessage);
});

需要注意的是,这里如果不带入this的话,resolve里面的this就会丢失。

但是这么写不优雅,我想了很多办法,比如重新包装一下,比如用事件响应,但还是解决不了,最后我突然想到,用bind,哇,成功解决。代码如下:

let Promise = function(func) {
this.func = func;
this.success = null;
this.error = null;
} Promise.prototype.resolve = function(value) {
if(typeof this.success == 'function') {
this.success(value);
}
} Promise.prototype.reject = function(value) {
if(typeof this.error == 'function') {
this.error(value);
}
} Promise.prototype.then = function(onFulfilled, onRejected) {
this.success = onFulfilled;
this.error = onRejected;
setTimeout(() => {
this.func(this.resolve.bind(this), this.reject.bind(this));
});
} let myPromise = new Promise(function(resolve, reject){
setTimeout(() => {
resolve("成功!", this);
}, 1000);
}); myPromise.then((successMessage) => {
console.log('输出', successMessage);
});

值得一提的是,为了实现惰性求值,需要先把异步方法缓存起来,等调用then的时候再调用它。

还有,在Promise内部,为了简便,我使用的是setTimeout进行异步,并没有使用setImmediate

实现all和race

all和race在多异步promise里面非常有用,下面我们来实现它们:

Promise.all = function(promiseArr) {
let results = [];
let sum = promiseArr.length;
let count = 0;
return new Promise(function(resolve, reject) {
if(promiseArr || sum) {
for(let i=0; i<sum; i++) {
promiseArr[i].then((res) => {
results[i] = res;
count ++;
if(count >= sum) {
resolve(results);
}
});
}
}
});
}; Promise.race = function(promiseArr) {
let sum = promiseArr.length;
let count = 0;
return new Promise(function(resolve, reject) {
if(promiseArr || sum) {
for(let i=0; i<sum; i++) {
promiseArr[i].then((res) => {
if(count == 0) {
count ++;
resolve(res);
}
});
}
}
});
};

可以看到,方法是使用传说中的哨兵变量,真的很有用。

测试

简易的测试代码如下:

let myPromise1 = new Promise(function(resolve, reject){
setTimeout(() => {
resolve("成功1111111!");
}, 1000);
}); let myPromise2 = new Promise(function(resolve, reject){
setTimeout(() => {
resolve("成功222222222222!");
}, 1500);
}); myPromise1.then((successMessage) => {
console.log('输出', successMessage);
}); console.time('all计时开始');
Promise.all([myPromise1, myPromise2]).then((results) => {
results.map(item => {
console.log('输出', item);
});
console.timeEnd('all计时开始');
}); console.time('race计时开始');
Promise.race([myPromise1, myPromise2]).then((res) => {
console.log('输出', res);
console.timeEnd('race计时开始');
});

可以看到,all计时刚好1.5秒,race计时刚好1秒。

我学到了什么

  1. 对Promise的理解更加深入。
  2. 对bind的使用更加熟练。
  3. 可以看到,Promise的缺点是对于每个异步方法,都需要用构造函数封装一遍,如果有其它需求,则需要更特别的封装。
  4. 打算找个时间用事件响应重新实现一遍,目前代码有点乱,如果用事件响应的话可能会更加优雅。

实现简易Promise的更多相关文章

  1. 简易promise

    <!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8" ...

  2. 简易promise的实现(二)

    code 上一章中我们遇到了两个问题 1.异步调用顺序的问题 2.then返回一个promise的问题 思考 如果控制异步回调的顺序? 因为异步操的时间作我们无法控制,但是我们只需要按顺序执行回调函数 ...

  3. 简易promise的实现(一)

    code 最近在思考promise的实现原理 于是准备自己写一个简单的demo 一开始想到的问题有两个 1.链式调用 2.异步顺序执行 -------------------------------- ...

  4. ES6之promise原理

    我在这里介绍了promise的原理: https://juejin.im/post/5cc54877f265da03b8585902 我在这里 仅仅张贴 我自己实现的简易promise——DiProm ...

  5. 用JavaScript完成页面自动操作

    在之前的一篇<JavaScript实现按键精灵>中曾记录了几个事件对象,本文将会对它们进行一次实战,要完成的动作包括滚动.点击和翻页. 一.滚动 滚动是通过修改容器元素的scrollTop ...

  6. Promise实现简易AMD加载器

    在最新的Chrome和FF中已经 实现了Promise.有了Promise我们用数行代码即可实现一个简易AMD模式的加载器 var registry = { promises: { }, resolv ...

  7. 10行代码实现简易版的Promise

    实现之前,我们先看看Promise的调用 const src = 'https://img-ph-mirror.nosdn.127.net/sLP6rNBbQhy0OXFNYD9XIA==/79910 ...

  8. 手写简易版Promise

    实现一个简易版 Promise 在完成符合 Promise/A+ 规范的代码之前,我们可以先来实现一个简易版 Promise,因为在面试中,如果你能实现出一个简易版的 Promise 基本可以过关了. ...

  9. 手写Promise简易版

    话不多说,直接上代码 通过ES5的模块化封装,向外暴露一个属性 (function(window){ const PENDING = 'pending'; const RESOLVED = 'fulf ...

随机推荐

  1. dotNet程序员的Java爬坑之旅(三)之spring MVC篇一

    使用maven構建springMVC項目,開發工具為IDEA 一.構建Maven項目,模板為WebApp 二.在pom文件中配置SpringMvc配置(springMvc需要以來servlet ,如本 ...

  2. mysql备份最近8天的数据库,老的自动删除方案

    服务器上的处理脚本记录: [root@mysql01 test]# crontab -l0 2 * * * /bin/sh /script/sqlbackup.sh >/dev/null 2&g ...

  3. 如何将一个excel表格的数据匹配到另一个表中

    我们在操作excel表的时,有时需要将一个excel表中的数据匹配到另一个表中,那么就需要用到VLOOKUP函数,VLOOKUP函数是Excel中的一个纵向查找函数,VLOOKUP是按列查找,最终返回 ...

  4. JS Object.defineProperties()方法

    JS Object.defineProperties()方法 描述: Object.defineProperties()方法为目标对象同时配置多个属性. 语法: Object.defineProper ...

  5. Linux关闭防火墙命令

    下面是red hat/CentOs7关闭防火墙的命令! 1:查看防火状态 systemctl status firewalld service  iptables status 2:暂时关闭防火墙 s ...

  6. OO前三次作业分析

    一,第一次作业分析 度量分析: 第一次的oo作业按照常理来说是不应该有这么多的圈复杂度,但是由于第一次写的时候,完全不了解java的相关知识,按照c语言的方式来写,完全的根据指导书的逻辑,先写好了正确 ...

  7. #2019-2020-4 实验二面向对象程序设计《Java开发环境的熟悉》实验报告

    2019-2020-4 实验二面向对象程序设计<Java开发环境的熟悉>实验报告 一.面向对象程序设计-1 ①实验要求: 1.参考 http://www.cnblogs.com/roced ...

  8. BP神经网络算法推导及代码实现笔记zz

    一. 前言: 作为AI入门小白,参考了一些文章,想记点笔记加深印象,发出来是给有需求的童鞋学习共勉,大神轻拍! [毒鸡汤]:算法这东西,读完之后的状态多半是 --> “我是谁,我在哪?” 没事的 ...

  9. PHP PDO预定义常量

    以下常量由本扩展模块定义,因此只有在本扩展的模块被编译到PHP中,或者在运行时被动态加载后才有效. 注意: PDO使用类常量自PHP 5.1.以前的版本使用的全局常量形式PDO_PARAM_BOOL中 ...

  10. 理解RNN

    摘自:https://zybuluo.com/hanbingtao/note/541458 语言模型就是这样的东西:给定一个一句话前面的部分,预测接下来最有可能的一个词是什么. 语言模型是对一种语言的 ...