当前位置: 首页 > 编程日记 > 正文

异步编程之Promise(2):探究原理

异步编程系列教程:

  1. (翻译)异步编程之Promise(1)——初见魅力
  2. 异步编程之Promise(2):探究原理
  3. 异步编程之Promise(3):拓展进阶
  4. 异步编程之Generator(1)——领略魅力
  5. 异步编程之Generator(2)——剖析特性
  6. 异步编程之co——源码分析

动手实现Promise

在异步编程之Promise(1)里,我是翻译了一篇文章,里面是探究promise的模式和领略它的魅力。我们可以利用promise,缓解回调函数给我们带来的回调金字塔。使用链式结构书写,使代码更加简洁易懂,易于控制。但是对于构造promise和其内部的实现,却用草草的一句new Promise()就带过。这一次,借着阅读朴灵大神的《深入浅出Node.Js》,我们自己动手实现一个小小的基本的promise吧。

构建Promise对象

首先我们需要回顾一下,一个Promise/A模式和API上是如何定义的:

  • Promise分别有三个状态:pending初始状态,fulfilled完成状态,rejected失败状态。
  • 一旦promise是fulfilled状态或rejected状态,那么它就是不会再改变的。
  • 具备then()方法,用于接收fulfilled和rejected状态的回调方法,并在相应状态下进行触发。
  • then()方法只允许接受function对象,其余的会被忽略。
  • then()方法会返回Promise对象,提供链式调用。
  • then()方法可接收第三个方法,用于支持progress事件的回调方法。

知道我们的Promise对象需要有什么之后,我们就可以开始尝试写Promise的构造函数了。还有Promise是基于事件机制的,也可以说是发布/订阅模式。所以我们为了演示方便,将使用Node里的events模块。

还不清楚自定义事件的同学,推荐一个视频给你们入门:阿当大话西游之WEB组件

var events = require('events'); //events模块
var util = require('util');     //util工具包模块var MyPromise = function(){events.EventEmitter.call(this); 
};
util.inherits(MyPromise, events.EventEmitter); // 继承MyPromise.prototype.then = function(resolve, reject, progress){// this.once()是绑定事件被触发后立即移除事件if(typeof resolve === 'function'){this.once('success', resolve);}if(typeof reject === 'function'){this.once('error', reject);}if(typeof progress === 'function'){// 不需要once()this.on('progress', progress);}return this;
};

由此,我们就实现了Promise/A规范。我们用promise对象的then,用相应的事件存放了各个状态的回调函数。那接下来,我们就要知道如何触发这些事件。

构建Deferred对象
---
为了实现事件的触发,我们需要有一个新的对象Deferred。意思是,延迟对象。

var Deferred = function(){this.state = 'pending';this.promise = new MyPromise();
};Deferred.prototype.resolve = function(obj){this.state = 'fulfilled';this.promise.emit('success', obj);
};
Deferred.prototype.reject = function(err){this.state = 'failed';this.promise.emit('error', err);
};
Deferred.prototype.progress = function(data){this.promise.emit('progress', data);
};

我们可以看到,我们之前定义的promise成为了deferred对象中的一个属性。然后Deferred对象的方法,都是用来触发事件来改变promise状态的。这种模式也称作Promise/Deffered模式,它是基于发布与订阅模式,并提供了更加高级的抽象。Deferred对象,用来控制Promise内部,维护Promise状态。Promise对象,则是作用于外部,通过then(resolve, reject)对外提供接口。

对于上一篇讲到的promise化的readJSON,我们可以使用我们定义的Promise/Deferred重写一遍:

var readJSON = function(filename, encoding){var deferred = new Deferred();fs.readFile(filename, encoding, function(err, res){if(err)return deferred.reject(err);deferred.resolve(JSON.parse(res));});return deferred.promise;
};// 应用
readJSON('data.json', 'utf-8').then(function(res){console.log(res.message); // Hello World!
})

对我来说,我更喜欢Promise/Deferred的实现。因为通过Deferred对象,我们可以很随心的控制promise的状态,得到我们想要的样子。当然喜欢原生ES6的那种为Promise构造函数传入工厂函数,也是可以自己改造一下的,和回调参数差不多,可以自行尝试一下。不知道是不是应为我个人水平问题,写起来觉得乱糟糟。所以我更喜欢Promise/Deferred模式的实现。代码如下:

// 去掉Deferred对象,直接通过回调参数来确定是resolve还是reject。
var MyPromise = function(factory){events.EventEmitter.call(this);var _this = this;factory && factory(function(res){console.log(res);_this.emit('success', res);}, function(err){_this.emit('error', err);});
};
util.inherits(MyPromise, events.EventEmitter);// 模拟ES6构造函数方法的应用
var readJSON = function(filename, encoding){return new MyPromise(function(resolve, reject){fs.readFile(filename, encoding, function(err, res){if(err)return reject(err);resolve(JSON.parse(res));});});
};readJSON("data.json", 'utf-8').then(function(data){console.log(data.message); // Hello World!
});

我们通过把json解析后传到resolve()中实现了我们上一篇的readJSON函数promise化的要求!酷~

开始使用Promise

从这两篇promise的探究路上,如果能体会到其中的奥妙,应该也差不多可以上道了。这个时候在ES6还未普及前,实现完整优秀promise模式可以借助一些promise库。这里我推荐 Q.js,为了能体现它的高效和优雅,我们借助以往的readJSON例子。

var Q = require('q');
var fs = require('fs');var readFile = function(filename, encoding){var deferred = new Q.defer(); // 获取Q的deferred对象fs.readFile(filename, encoding, function(err, res){if(err)return deferred.reject(err);deferred.resolve(res);});return deferred.promise;   // 将promise对象return出去,实现链式调用
};var readJSON = function(filename, encoding){return readFile(filename, encoding).then(JSON.parse);
};readJSON('data.json', 'utf-8').then(function(data){console.log(data.message); //Hello World!
});

这里并没有体现优雅,但是可以看到promise/deferred模式的使用。特别是和我一样喜欢这种模式的同学,简直不能再爽!当然,说到优雅,我们可以回想一下,每次我们处理fs.readFile()callback时,我们都是重复的有错误就reject,没错误就resolve。同样的逻辑,其实我们是可以封装起来的,Q就帮我们做到了这一点。

// 改变fs.readFile()
var readFile = function(filename, encoding){var deferred = new Q.defer(); // 获取Q的deferred对象    fs.readFile(filename, encoding, deferred.makeNodeResolver());return deferred.promise;   // 将promise对象return出去,实现链式调用
};

语意可得,弄一个Node式的回调。这个实现其实很简单,书上也有说。这个就交给各位同学们,自己动手试一试吧~

接下来,会针对我们自己实现的Promise进行拓展,完成我们更多的需求。在此过程中,探究Promise实现原理。为接下来的异步编程学习打下基础。

再次感谢朴灵老师的《深入浅出Node.Js》。

再次感谢朴灵老师的《深入浅出Node.Js》。

再次感谢朴灵老师的《深入浅出Node.Js》。

重要的话,要说三遍。

转载于:https://www.cnblogs.com/YikaJ/p/4468987.html

相关文章:

PC端、移动端手机竖拍原图压缩上传顺时针旋转90°的解决方案

问题背景 最近在做的项目中,不管是移动端还是后台系统都涉及到了手机照片压缩上传的问题,做完功能测试的时候发现图片回显的时候有些顺时针旋转了90(竖拍照片,不管是ios还是android都存在这问题),后来百度了…

perl:cpanm安装方式的一种取代方法

笔者现在有一个cpanfile,里面需要安装的perl模块,github的作者给出的指令是 cpanm --installdeps . 而笔者的服务器没有cpanm,因为不是管理员也无法用apt安装 经过一番搜索,发现以下的替代方式: 依次在命令行输入 1…

elementUI源码修改的爬坑之旅

今天由于项目需要,想在Tree组件的前面增加一个icon图标,根据不同类型增加不同的图标,我修改了elementUI的源代码,发布到npm上去成功使用,记录下过程中所碰到的问题,首先看下最后的效果: 下面简单…

Sublime text3 快捷方式(windows平台)

1.Goto Anything-快速查找(ctrl P) 输入函数名可以快速找到函数输入#文本可以快速进行文件内文本匹配2.命令模式CtrlShiftP:打开命令面板 CtrlP:搜索项目中的文件 CtrlW:关闭当前打开文件 CtrlShiftW&#xf…

(已解决)ubuntu20.04 jar xvf XXX.zip报错-java.io.EOFException:Unexpected end of ZLIB input stream

报错截图 如下 我在服务器上解压缩defects4j-repos.zip,但是出现了如上错误。通过SFTP我把这个压缩包传到了本地win10上面,进行解压缩。本地解压缩软件(有图形界面的好处显现了)告诉我这个文件已经损坏。 原因分析 由于运行脚本涉及到一堆的文件下载&am…

Linux上传和下载

下载:sz 上传:rz

joomla网页无任何显示输出

joomla配置环境:LAMP 先打开php.ini display_errorsOn 网页显示报错 Class DOMDocument not found *********** 解决方法: 1.安装php-xml #yum install php-xml 2.重启apache(CentOS) #service httpd restart转载于:https://www…

【mysql的编程专题⑤】自定义函数

用户自定义函数(user-defined function,UDF) 是一种对mysql的扩展途径,其用法与内置函数相同 创建自定义函数 语法 create function function_name returns {string|integer|real|decimal} routine_body Example1 delimiter $ create function f1(gdate datetime) …

配深度学习环境要注意的不多,也就

1. 使用nvcc -V命令查看cuda版本,再在官网选择对应版本的pytorch。(是的不要根据python的版本去选择pytorch指令) 2. 用conda安装pytorch可能一直有cpuonly的玄学问题。(哪怕选择的是CUDA版本的pytorch安装命令)改用pip安装。 3. torchtext不会被顺带安装&#xf…

整理的一点MD5资料(第一部分)

MD5加密算法研究MD5简介: MD5的全称是Message-Digest Algorithm 5,在90年代初由MIT的计算机科学实验室和RSA Data Security Inc发明,经MD2、MD3和MD4发展而来。 Message-Digest泛指字节串(Message)的Hash变换,就是把一个任意长度的…

SQL学习(六)

1. INSERT 通用形式: INSERT INTO tablename [colname {,colname...}] {VALUES (expr |NULL | Subquery)} 例:1. insert into orders (ordno,month) values (1107.aug); 2. insert into swcusts select * from customers wh…

百练,4103,踩方格

百练,4103,踩方格 普通做法:(也可以找规律) #include #include//要调用memset函数,头文件 using namespace std; int visited[50][50];//开辟50*50的方格 int num0;//num为方案数 void ways(int i,int j,i…

BZOJ1747 [Usaco2005 open]Expedition 探险

首先我们可以发现如果错过了一个加油站,而继续往前走的时候没有油了,可以再假装之前经过加油站的时候加过油 于是我们维护一个大根堆,表示错过的加油站是哪些,每当没有油的时候从堆顶取出最大值加上去即可 1 /*******************…

Netty - ByteBuf索引管理

2019独角兽企业重金招聘Python工程师标准>>> 之前写过一篇文章介绍过JDK里面InputStream的mark(readlimit)和reset()方法,Java IO:使用mark/reset实现替换文件中字符串。相应的,可以通过调用markReaderIndex()/markWriterIndex()/…

DB2 9 利用开辟(733 测验)认证指南,第 1 部分: 数据库工具与编程步调(6)

运用基泉源基本理构建根本完毕语总结本教程向您引见了 DB2 利用挨次开辟的根本常识。在中缀任何开辟变乱之前,必须熟习差别规范的数据库工具。在本教程中,引见了几种初级的数据库工具。别名、序列工具和且则表凡是用于简化利用挨次代码。存储历程、函数和…

PHP 设计模式 笔记与总结(9)数据对象映射模式

【数据对象映射模式】 是将对象和数据存储映射起来,对一个对象的操作会映射为对数据存储的操作。例如在代码中 new 一个对象,使用数据对象映射模式就可以将对象的一些操作比如设置一些属性,就会自动保存到数据库,跟数据库中表的一…

inet_pton和inet_ntop函数

Linux下这2个IP地址转换函数&#xff0c;可以在将IP地址在“点分十进制”和“整数”之间转换 而且&#xff0c;inet_pton和inet_ntop这2个函数能够处理ipv4和ipv6。算是比较新的函数了。 inet_pton函数原型如下[将“点分十进制” &#xff0d;> “整数”] #include <sys/…

Firefox3 RC1颁布各种新特征发扬阐发更平定

作者: Dailon 出自: http://www.linuxdiyf.com Mozilla项目曾经颁布了Firefox 3 RC1。Firefox 3的新特征包罗对Javascript 1.8的支持、DOM和HTML的转变、微技俩&#xff08;microformats&#xff09;支持及一个扩展的Canvass完成。 Mozilla公司颠末对Javascript 1.8特征…

java中判断两个字符(或者字符串相等)

string a,b;//两字符串 在java中判断两个字符&#xff08;字符串&#xff09;相等&#xff0c;用a.equals(b); if(a.equals(b)){ //如果相等&#xff0c;返回值为true }else{ //如果不相等&#xff0c;返回值为false } 不能用if(ab)来进行判断,这里比较的是两数据的内存

初学Hadoop之图解MapReduce与WordCount示例分析

Hadoop的框架最核心的设计就是&#xff1a;HDFS和MapReduce。HDFS为海量的数据提供了存储&#xff0c;MapReduce则为海量的数据提供了计算。HDFS是Google File System&#xff08;GFS&#xff09;的开源实现&#xff0c;MapReduce是Google MapReduce的开源实现。HDFS和MapReduc…

cnblogs不愧为cnblogs

在cnblogs落脚3天了&#xff0c;写了一些很实践的文章&#xff0c;很有成就感&#xff0c;为什么&#xff1f;我第一次为是否发表这篇文章而作出思考&#xff0c;以前在别的地方&#xff0c;总是转别人的文章居多&#xff0c;到了cnblogs&#xff0c;感受到了原创的精神——即使…

Debian刊行版3晋级到4

作者: rincess 出自: http://www.linuxdiyf.com 前些天把供职器从Debian 3 sarge晋级到Debian 4 etch&#xff0c;经过议定考察这几天的运转浮现&#xff0c;感受非常满意。新的体系包孕了新的WWW供职器Apache 2.2和PHP 5.2.0&#xff0c;SVN也从1.1晋级到了1.4&#xff0c;Tr…

简单五子棋问题,java实现

**题目要求&#xff1a;**实现一个控制台下五子棋的程序。用一个二维数组模拟一个15*15路的五子棋棋盘&#xff0c;把每个元素赋值位“┼”可以画出棋盘&#xff0c; “○”代表该交叉点下了一颗白棋子&#xff0c;“●”代表该交叉点下了一颗黑棋子。每次用户输入要下棋的坐标…

Singleton 单例模板

1 // singleton.h2 3 #ifndef SINGLETON_H4 #define SINGLETON_H5 6 // 单例基类模板7 template <class T>8 class Singleton9 { 10 public: 11 static T& give_me() 12 { 13 static T s_inst; 14 return s_inst; 15 } 16 17 private: …

Oracle 7.3.4 for OpenServer 5 装配

起原&#xff1a;网海拾贝 1.建立oracle用户、dba组&#xff1b;2.调整系统中央参数&#xff1a;#./idtune SHMMAX 409600000#./idtune SEMMNI 153.装配SCO补丁文件 oss459A&#xff0c;重新毗连中央并重新启动&#xff1a;#/etc/conf/cf.d/link_unix -y#init 6   4.编纂oracl…

sql server中的hash应用优化

sql server很多地方用到hash&#xff0c;计划缓存查找&#xff0c;hash连接&#xff0c;hash聚合等。 hash查找mssql很多设计中都应用了&#xff0c;不过我们能在开发设计中&#xff0c;也可以利用这个特性。 将如果有需求对大文本进行完全匹配&#xff0c;比如查找产品型号&am…

JAVA猴子选大王问题

题目要求 猴子选大王。输入猴子的个数n&#xff0c;所有的猴子从1-n编号排成一圈&#xff0c;从1号猴子开始数数&#xff0c;数到3的倍数猴子被淘汰&#xff0c;最后剩下的是大王。输出是大王的猴子的编号&#xff08;提示&#xff1a;使用数组&#xff0c;每个数组元素相当于一…

POJ 1185 炮兵阵地 (状压DP)

炮兵阵地Time Limit: 2000MS Memory Limit: 65536KTotal Submissions: 14869 Accepted: 5575Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成&#xff0c;地图的每一格可能是山地&#xff08;用"H" 表示&#xff09;…

不雅测发挥分析Android在美智能机市场凌驾黑莓及苹果

网易科技讯 3月4日动静&#xff0c;根据尼尔森公司比来宣布的陈诉发挥分析&#xff0c;Android操纵体系以29%的市占率在美国智好手机市场凌驾黑莓(27%)和苹果(27%)。其中&#xff0c;宏达电占12%&#xff0c;摩托罗拉占10%&#xff0c;三星占5%。但从厂商角度来看&#xff0c;苹…

Java基础-常量,变量,成员变量,局部变量

在java中&#xff0c;数据是以常量和变量两种方法形式进行存储和表示的&#xff08;实际上&#xff0c;所有程序的数据都是这两种形式&#xff09;。 变量 变量代表程序的状态。程序通过改变变量的值来改变整个程序的状态&#xff0c;或者说得更大一些&#xff0c;也就是实现程…