实现简易Promise
概述
异步编程离不开promise, async, 事件响应这些东西,为了更好地异步编程,我打算探究一下promise的实现原理,方法是自己实现一个简易的promise。
根据promise mdn上的描述,我们主要实现如下api:
- Promise.prototype.resolve
- Promise.prototype.reject
- Promise.prototype.then
- Promise.all
- 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秒。
我学到了什么
- 对Promise的理解更加深入。
- 对bind的使用更加熟练。
- 可以看到,Promise的缺点是对于每个异步方法,都需要用构造函数封装一遍,如果有其它需求,则需要更特别的封装。
- 打算找个时间用事件响应重新实现一遍,目前代码有点乱,如果用事件响应的话可能会更加优雅。
实现简易Promise的更多相关文章
- 简易promise
<!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8" ...
- 简易promise的实现(二)
code 上一章中我们遇到了两个问题 1.异步调用顺序的问题 2.then返回一个promise的问题 思考 如果控制异步回调的顺序? 因为异步操的时间作我们无法控制,但是我们只需要按顺序执行回调函数 ...
- 简易promise的实现(一)
code 最近在思考promise的实现原理 于是准备自己写一个简单的demo 一开始想到的问题有两个 1.链式调用 2.异步顺序执行 -------------------------------- ...
- ES6之promise原理
我在这里介绍了promise的原理: https://juejin.im/post/5cc54877f265da03b8585902 我在这里 仅仅张贴 我自己实现的简易promise——DiProm ...
- 用JavaScript完成页面自动操作
在之前的一篇<JavaScript实现按键精灵>中曾记录了几个事件对象,本文将会对它们进行一次实战,要完成的动作包括滚动.点击和翻页. 一.滚动 滚动是通过修改容器元素的scrollTop ...
- Promise实现简易AMD加载器
在最新的Chrome和FF中已经 实现了Promise.有了Promise我们用数行代码即可实现一个简易AMD模式的加载器 var registry = { promises: { }, resolv ...
- 10行代码实现简易版的Promise
实现之前,我们先看看Promise的调用 const src = 'https://img-ph-mirror.nosdn.127.net/sLP6rNBbQhy0OXFNYD9XIA==/79910 ...
- 手写简易版Promise
实现一个简易版 Promise 在完成符合 Promise/A+ 规范的代码之前,我们可以先来实现一个简易版 Promise,因为在面试中,如果你能实现出一个简易版的 Promise 基本可以过关了. ...
- 手写Promise简易版
话不多说,直接上代码 通过ES5的模块化封装,向外暴露一个属性 (function(window){ const PENDING = 'pending'; const RESOLVED = 'fulf ...
随机推荐
- 5N - 考试排名
C++编程考试使用的实时提交系统,具有即时获得成绩排名的特点.它的功能是怎么实现的呢? 我们做好了题目的解答,提交之后,要么“AC”,要么错误,不管怎样错法,总是给你记上一笔,表明你曾经有过一次错误提 ...
- github node.js
#安装githubyum install git -y #下载nvmgit clone git://github.com/creationix/nvm.git ~/nvm #设置nvm 自动运行;ec ...
- ASP.NET Core Web API 如何 数据分页 以及遇到'OFFSET' 附近有语法错误
最近领导叫我做的一个B/S端的小项目,突发奇想想用到core web api 今天写数据分页的时候,就想着 用linq分页查询吧,直接上代码 _context.Skip(Size * (PageNum ...
- 20170529计划---统计业务量并生成EXCEL通过邮件发送
每个月都要统计这些业务量的东东,烦死了,赶紧通过python写一个来搞定吧,三天搞定吧,未完待续哈. 2017-5-29 19:50粗略地做了一个思维导图哈 终于第三天完成啦 #encoding=ut ...
- 遍历ArcMap已加载数据的属性
import arcpy f = open("D:\workspace\coords.txt","w") with arcpy.da.SearchCursor( ...
- Js 常用调试的方法
A 使用alert() 和document.write() 方法监视变量值 如果要中断代码的运行,监视变量的值,则使用alert() 方法: 如果需要查看的值很多,则使用document.write ...
- ubuntu16.04安装tensorflow1.3
总结 : 1.点软件个更新-系统更新2.降级gcc到5.33.装CUDA及第二个包,加入PATH4.CUDNN5.Ancada..6.TF Ubuntu16.04 的GCC版本降级 http://bl ...
- 距离不是一个连续的物理量(Distance is not a continuous physical quantity)
量子距:不同于现有物理学的长度计量.量子距,空间中的两个粒子之间的距离并不是连续的,而是某个单位距(量子单位距)的整数倍,而这个距离被称为量子距. Quantum distance: Length m ...
- 《Linux就该这么学》第十四天课程
samba服务的配置文件解读 samba服务解决了Linux系统与Windows系统之间的文件共享问题,是一个非常不错的服务 原创地址:https://www.linuxprobe.com/chapt ...
- JDBC学习(含转载)
首先连接数据库: Class.forName("com.mysql.jdbc.Driver");//加载jdbc驱动 Connection conn=DriverManager.g ...