Nodejs Q promise设计思路
Nodejs Q promise库
前言
Q库为nodejs提供了一个基于promise的编程方式,从此避免了一层又一层的callback调用。不过Q的灵活性也给我造成了很大困扰,我可以用promise去resolve promise么,我在then里return promise可以么?因此我研究了下Q库的源代码,幸运的是,Q库提供了一份详细的设计文档,极大的方便了我们对其设计思路的理解。
正文
跳过一些最原始的版本,我们先看看根据promise的最基本工作方式,能想到的最简单的promise实现。
var defer = function () {
var pending = [], value;
return {
resolve: function (_value) {
value = _value;
for (var i = 0, ii = pending.length; i < ii; i++) {
var callback = pending[i];
callback(value);
}
pending = undefined;
},
then: function (callback) {
if (pending) {
pending.push(callback);
} else {
callback(value);
}
}
}
};
在defer里存储函数,传递到then里的callback会在原defer被resolve时触发,这是最直观的promise实现方式。
这种实现方式无法实现链接,defer.promise.then.then.then是不能实现的,另外defer.resolve只能用实值而不能用promise。
为了能够将promise链接起来,then函数必须也return一个promise,为了能够用promise去resolve defer。q提供了一个对实值的封装。
var ref = function (value) {
if (value && typeof value.then === "function")
return value;
return {
then: function (callback) {
callback(value);
}};
};
如果value本身为promise,就返回该promise,如果该value为实值,就返回一个已经被resolve了的promise,resolve value就是这个实值。
var ref = function (value) {
if (value && typeof value.then === "function")
return value;
return {
then: function (callback) {
return ref(callback(value));
}};
};
这个版本将callback的返回值也封装成promise,因此then函数的返回值可以继续用then继续链接下去。不过我们还没法用promise去resolve defer,因此引出了下面这个版本。
var defer = function () {
var pending = [], value;
return {
resolve: function (_value) {
if (pending) {
value = ref(_value); // values wrapped in a promise
for (var i = 0, ii = pending.length; i < ii; i++) {
var callback = pending[i];
value.then(callback); // then called instead
}
pending = undefined;
}
},
promise: {
then: function (_callback) {
var result = defer();
// callback is wrapped so that its return
// value is captured and used to resolve the promise
// that "then" returns
var callback = function (value) {
result.resolve(_callback(value));
};
if (pending) {
pending.push(callback);
} else {
value.then(callback);
}
return result.promise;
}
}
};
};
在resolve一个defer时,defer会将该value封装成一个promise。然后会将目前存储的callbacks传递到该promise的then函数中,callback函数的调用时机取决于该promise的resolve时刻了。
注意,这里存储的callback也不是通过then函数传递进来的callback了,为了实现then的链接,存储的callback做的实际事情是调用传递进来的callback,并且用结果去resolve内部新建的一个defer,并且then函数返回该新建defer的promise。
简而言之,then里面传递的callback函数也可以返回一个promise,之后的then链里只有在该promise被resolve时才会被调用。
因此现在我们的q库可以写出如下的调用方式:
var deferA = q.defer();
deferA.promise //promiseA
.then(function(value){
//func1
return promise2;
})
.then(function(value){
//func2
return promise3;
});
defer.resolve(promise1);
这套函数会构造出一个链条:
deferA ->promiseA -> hidden_deferA -> call func1 -> promise2 -> hidden_defer2 -> call func2 -> promise3
也就是说,每次对一个promise调用then时,它就会创建一个隐藏的defer,返回这个defer的promise,当原promise被resolve时,该defer也会被传递进去的函数生成的结果resolve。
到这一步为止,就是q的promise的大概实现原理了。然而q为了进一步抽象和提高,做出了一个message机制,以后的文章再仔细研究。
Nodejs Q promise设计思路的更多相关文章
- angular $q promise详解
前言 通过本文,你大概能清楚angular promise是个啥,$q又是个啥,以及怎么用它.这里咱们先灌输下promise的思想. 下面写的全是废话,一些看着高逼格其实没什么大作用的概念,想知道$q ...
- IM开发基础知识补课(七):主流移动端账号登录方式的原理及设计思路
1.引言 在即时通讯网经常能看到各种高大上的高并发.分布式.高性能架构设计方面的文章,平时大家参加的众多开发者大会,主题也都是各种高大上的话题——什么5G啦.AI人工智能啦.什么阿里双11分分钟多少万 ...
- TYPESDK手游聚合SDK服务端设计思路与架构之一:应用场景分析
TYPESDK 服务端设计思路与架构之一:应用场景分析 作为一个渠道SDK统一接入框架,TYPESDK从一开始,所面对的需求场景就是多款游戏,通过一个统一的SDK服务端,能够同时接入几十个甚至几百个各 ...
- 分享一个CQRS/ES架构中基于写文件的EventStore的设计思路
最近打算用C#实现一个基于文件的EventStore. 什么是EventStore 关于什么是EventStore,如果还不清楚的朋友可以去了解下CQRS/Event Sourcing这种架构,我博客 ...
- ENode框架单台机器在处理Command时的设计思路
设计目标 尽量快的处理命令和事件,保证吞吐量: 处理完一个命令后不需要等待命令产生的事件持久化完成就能处理下一个命令,从而保证领域内的业务逻辑处理不依赖于持久化IO,实现真正的in-memory: 保 ...
- WebGIS中快速整合管理多源矢量服务以及服务权限控制的一种设计思路
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 在真实项目中,往往GIS服务数据源被其他多个信息中心或者第三方 ...
- OpenStack 通用设计思路 - 每天5分钟玩转 OpenStack(25)
API 前端服务 每个 OpenStack 组件可能包含若干子服务,其中必定有一个 API 服务负责接收客户请求. 以 Nova 为例,nova-api 作为 Nova 组件对外的唯一窗口,向客户暴露 ...
- Redis入门指南(第2版) Redis设计思路学习与总结
https://www.qcloud.com/community/article/222 宋增宽,腾讯工程师,16年毕业加入腾讯,从事海量服务后台设计与研发工作,现在负责QQ群后台等项目,喜欢研究技术 ...
- MVC3 数据验证用法之密码验证设计思路
描述:MVC数据验证使用小结 内容:display,Required,stringLength,Remote,compare,RegularExpression 本人最近在公司用mvc做了一个修改密码 ...
随机推荐
- 在VMWare的虚拟机中设置共享文件夹(Linux-Ubuntu系统)
1.点击菜单中的虚拟机---设置---选项---共享文件夹,选择总是开启: 2.添加主机共享的文件夹: 3.安装vmware tools: (1)打开虚拟机---安装vmware tools: (2) ...
- Appium+python移动端自动化测试-python库及pycharm安装(二)
一.安装python库 安装python库有很多种方法,如pip安装.源文件安装.运行包安装,但我们最常用的就是使用pip进行安装 Appium+python做移动端的自动化测试,所需要安装以下pyt ...
- Favorite Donut--hdu5442(2015年长春网选赛,kmp,最大表示法)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5442 打比赛的时候还没学kmp更没有学最大最小表示法,之后做完了kmp的专题,学了它们,现在再来做这道 ...
- Redis for Python开发手册
redis基本命令 String Set set(name, value, ex=None, px=None, nx=False, xx=False) 在Redis中设置值,默认,不存在则创建,存在则 ...
- javascrpt 页面格式化页面
下面这个页面,格式化javaScript <html> <head> <title>JS格式化工具 </title> <meta http-equ ...
- SpringBoot安装和创建简单的Web应用
SpringBoot安装 方式一: Eclipese->Help->Eclipse Marketplace ->Finde STS -> Install 注意:安装过程中挺慢, ...
- linux 配置SSH网络传输数据安全方案,JDK,Tomcat和Eclipes
一.通过ssh实现安全远程访问linux系统 ssh :secure shell 加密: 1. 对称加密 (加密密钥与解密密钥相同) des .aes 2. 非对称加密(加密密钥与解密密钥不同) RS ...
- 增强MyEclipse的代码自动提示功能
一般在Eclipse ,MyEclipse代码里面,打个foreach,switch等 这些,是无法得到代码提示的(不信自己试试),其他的就更不用说了,而在Microsoft Visual Stu ...
- 转Hibernate 一对多关联的CRUD__@ManyToOne(cascade=(CascadeType.ALL))
一:Group和Users两个类 假定一个组里有n多用户,但是一个用户只对应一个用户组. 1.所以Group对于Users是“一对多”的关联关系@OneToMany Users对于Group是“多对一 ...
- PAT 1036 Boys vs Girls[简单]
1036 Boys vs Girls (25 分) This time you are asked to tell the difference between the lowest grade of ...