异步编程系列教程:

  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

异步编程之Promise(2):探究原理相关推荐

  1. 前端异步编程之Promise和async的用法

    传统的异步解决方案采用回调函数和事件监听的方式,而这里主要记录两种异步编程的新方案: ES6的新语法Promise ES2017引入的async函数 Generator函数(略) Promise的含义 ...

  2. python3 sleep 并发_python异步编程之asyncio(百万并发)

    点击上方蓝字关注我们 目录 [python 异步编程之 asyncio(百万并发)] 一.asyncio 二.aiohttp 前言:python 由于 GIL(全局锁)的存在,不能发挥多核的优势,其性 ...

  3. pythonasyncio并发编程实战_python异步编程之asyncio(百万并发)

    [python异步编程之asyncio(百万并发)] 前言:python由于GIL(全局锁)的存在,不能发挥多核的优势,其性能一直饱受诟病.然而在IO密集型的网络编程里,异步处理比同步处理能提升成百上 ...

  4. 并发编程之 Executor 线程池原理与源码解读

    并发编程之 Executor 线程池原理与源码解读 线程是调度 CPU 资源的最小单位,线程模型分为 KLT 模型与 ULT 模型,JVM使用的是 KLT 模型.java线程与 OS 线程保持 1:1 ...

  5. ES6 异步编程之二:Promise

    异步回调的泥潭 异步回调是最直接的异步结果处理模式,将一个回调函数callback扔进异步处理函数中,当异步处理获得结果之后再调用这个回调函数就可以继续之后的处理,但是如果这个callback又是一个 ...

  6. JS异步编程之callback

    为什么 JS 是单线程? 众所周知,Javascript 语言的执行环境是"单线程"(single thread). 所谓"单线程",就是指一次只能完成一件任务 ...

  7. Node.js 异步编程之 Callback介绍

    原文:http://www.jb51.net/article/63070.htm ------------------------------------- Node.js 基于 JavaScript ...

  8. dojo异步编程之dojo/Deferred

    dojo/Deferred介绍 dojo/Deferred是一个类,是Dojo中管理异步线程的基础. 简单来说,一个Deferred对象会等待一段时间再去执行指定的调用,直到某个特定的事件发生或者前一 ...

  9. JS异步编程之Generator

    前言 ES6 中提出一个叫生成器(Generator)的概念,执行生成器函数,会返回迭代器对象(Iterator),这个迭代器对象可以遍历函数内部的每一个状态. function* helloWorl ...

最新文章

  1. linux top 命令信息解释
  2. python调用海康sdk 数据类型
  3. 新一代組合創新架構師_學習地圖
  4. php证书格式,常用的证书格式转换 - niceguy_php的个人空间 - OSCHINA - 中文开源技术交流社区...
  5. java与c/c++进行socket通信的一些问题
  6. 微信电脑客户端_无聊的话,用微信玩玩电脑
  7. camel apache_Apache Camel 3只有2个月的路程
  8. 用matlab绘制外部导入数据图像,matlab 外部数据导入方法详解(3)
  9. 【转载保存】hadoop三个配置文件的参数含义说明core-site.xml、hdfs-site.xml、...
  10. 分支程序设计02 - 零基础入门学习C语言11
  11. vc如何打开plt图像_opencv_python从zero到hero————之图像基本操作01
  12. 【Computer Organization笔记15】清华计组大作业布置:奋战二十天,做台计算机!
  13. 【代码质量】如何使用Valgrind检测内存泄漏
  14. python 求向量间内积 和外积
  15. Suspending Methods【暂停方法队列说明】
  16. Dining POJ - 3281 (网络流)
  17. redis incr mysql_INCR
  18. android 处理home键,android处理home键的方法
  19. 公众号运营该如何快速找到内容方向定位?
  20. 编写程序,用户输入一个列表和2个整数作为下标,然后输出列表中介于2个下标之间的元素组成的子列表。考试题库7

热门文章

  1. 贪心算法单源点最短路径例题c语言源代码,Dijkstra算法是解单源最短路径问题的一个贪心算法...
  2. java jdk 1.8 配置_Java开发环境jdk 1.8安装配置方法(Win7 64位系统/windows server 2008)...
  3. 该文件 linux命令,Linux网络系统,如果执行行命令#chmod 746 file.txt,那么该文件的权限是?...
  4. 在内网中使用maven_maven构建docker镜像三部曲之三:推送到远程仓库(内网和阿里云)-Go语言中文社区...
  5. 厦门理工计算机研究生调剂,2018年厦门理工大学考研预调剂信息公布
  6. linux 修改mysql root密码_Linux mysql如何更改root密码
  7. 计算机二级应用题改卷,全国计算机等级考试二级Python语言程序设计模拟试卷B卷综合应用题-Go语言中文社区...
  8. cs oracle语句跟踪,Oracle执行语句跟踪 使用sql trace实现语句追踪
  9. php进程通讯 windows,windows-server-2008 – PHP进程一次运行一个,总是占用一个核心的100%...
  10. MySQL面试题 | 附答案解析(十一)