JavaScript Promises
上篇文章介绍了JavaScript异步机制,请看这里。
JavaScript异步机制带来的问题
JavaScript异步机制的主要目的是处理非阻塞,在交互的过程中,会需要一些IO操作(比如Ajax请求,文件加载,Node.js中的文件读取等),如果这些操作是同步的,就会阻塞其它操作。
异步机制虽然带来了许多好处,但同时也存在一些不如意的地方。
代码可读性
这样的代码读起来简直累觉不爱啊~~~
operation1(function(err, result) {
operation2(function(err, result) {
operation3(function(err, result) {
operation4(function(err, result) {
operation5(function(err, result) {
// do sth.
})
})
})
})
})
流程控制
异步机制使得流程控制变的有些困难,比如,在N个for循环中的回调函数执行完成之后再做某些事情:
var data = [];
fs.readdir(path, function (err, files) {
if (err) {
console.log(err)
} else {
for (var i in files) {
(function (i) {
fs.stat(path + '/' + files[i], function (err, stats) {
if (err) {
console.log(err)
} else {
o = {
"fileName": files[i].slice(0, -5),
"fileTime": stats.mtime.toString().slice(4, 15)
};
data.push(o);
}
})
})(i);
}
}
});
var html = template('templates/main', data);
res.writeHead(200, {'Content-Type': 'text/html; charset="UTF-8"'});
res.write(html);
res.end();
上面的代码不能获得预期的结果,因为for循环中所有的fs.stat执行结束后,data才会获得预期的值。可是,怎么知道for循环全部执行结束了呢?
异常处理
再看看上面的代码,如果多几个需要处理异常的地方,代码可谓支离破碎了。
Promises
Promise是对异步编程的一种抽象。它是一个代理对象,代表一个必须进行异步处理的函数返回的值或抛出的异常。
Promises全称叫Promises/A+,是一个开放的JavaScript规范,已经被加入ES6中。Promises只是一种规范,从Chrome32起开始支持Promises。
已经实现这个标准的库有Q、when.js、Dojo.deferred、jQuery.deferred等。
鉴于浏览器已经支持Promises,所以尽量使用原生的Promises,对于不支持Promises的浏览器,建议使用这个polyfill: promise-5.0.0.js,Promises其实很简单:
function a (num) {
var promise = new Promise(function (resolve, reject) {
var count = num;
if (count >= 10) {
setTimeout(function () {
count ++;
console.log(count);
resolve(count);
}, 1000);
} else {
reject(count);
}
});
return promise;
}
function b (count) {
var promise = new Promise(function (resolve, reject) {
setTimeout(function () {
count *= 10;
console.log(count);
resolve(count);
}, 1000);
});
return promise;
}
function c () {
console.log('done');
}
function alert(num) {
console.log('您输入的数字为' + num + ',不能小于10!');
}
a(11)
.then(b, alert)
.then(c);
上面这段代码做的事情是:输入num,1s后输出num + 1,2s后输出(num + 1) * 10,最后输出done。
相比a、b两个方法本身,通过Promises定义的方法多了这些东西:
定义一个promise。
var promise = new Promise(function (resolve, reject) {));
完成方法的功能时,调用resolve(data)。
resolve(count);
最后返回promise。
return promise;
首先熟悉一下Promises是怎样定义promise状态的。Promises/A+是这样规定的:
- 一个promise必须是下面三种状态之一:pending, fulfilled, rejected
- 当一个promise是pending状态:
- 可以转变到fulfilled状态或者rejected状态
- 当一个promise是fulfilled状态:
- 不可以转变到其他任何状态
- 必须有一个不能改变的value
- 当一个promise是rejected状态:
- 不可以转变到其他任何状态
- 必须有一个不可改变的reason
上面代码在a()中使用new Promise()实例化了一个promise后,这个promise默认状态是pending。
promise.then()方法有2个参数,分别是onFulfilled、onRejected,这2个参数都是方法名(也就是回调函数),通过这2个参数可以对应promise的fulfilled和rejected2种状态。
通过上面的代码来解释就是:
当a()正常执行结束时,调用resolve(data)将promise的状态改变为fulfilled,并且通过then()的第一个参数onFulfilled将参数data传递给下一个方法b()。
当a()非正常结束,这里认为a()在执行过程中出现了异常时,调用reject(reason)将promise的状态改变为rejected,并且通过then()的第二个参数onRejected将参数reason传递给下一个方法alert()。
在前面说到流程控制的时候提到的for循环的问题还没有解决:如果想等N个for循环中的回调函数执行结束之后做某些事情,该怎么办?
这时候该用到Promise.all()方法了,比如前面提到的一段代码,需求是这样:
在Node.js中,创建http服务器,读取当前目录下articles目录中的所有文件,遍历所有文件,并根据“目录+文件名”读取文件的最后修改时间,最终返回[{文件名,文件修改时间}, {文件名,文件修改时间}, ...]
这样一个列表到客户端。
这里存在的问题是,读取目录的操作是异步的,for循环读取文件状态的操作也是异步的,而在for循环中的所有异步操作都执行结束后,需要调用response.writeHead()与response.write()将所有异步数据返回到客户端。在使用when.js之前,我能想到的就是把for循环中的异步操作变为同步操作,最后再返回数据,但是就会阻塞其他的同步操作,显然这违背了异步机制。
利用Promise.all()改造过后的代码:
var http = require('http'),
fs =require('fs'),
connect = require('connect'),
Promise = require('promise');
function readDir (path) {
return new Promise(function (resolve, reject) {
fs.readdir(path, function (err, files) {
if (err) {
reject(err);
} else {
resolve({
"path": path,
"files": files
});
}
});
});
}
function getFileStats (data) {
var
files = data.files,
promiseList = [];
for (var i in files) {
(function (i) {
var promise = new Promise(function (resolve, reject) {
fs.stat(data.path + '/' + files[i], function (err, stats) {
if (err) {
reject(err)
} else {
var o = {
"fileName": files[i].slice(0, -5),
"fileTime": stats.mtime.toString().slice(4, 15)
};
resolve(o);
}
})
});
promiseList.push(promise);
})(i);
}
return Promise.all(promiseList);
}
var app = connect()
.use(function(req, res) {
if (req.url === '/favicon.ico') {
return;
} else {
readDir('articles/fe').then(getFileStats)
.then(function (o) {
res.writeHead(200, {'Content-Type': 'text/html; charset="UTF-8"'});
res.write(JSON.stringify(o));
res.end();
});
}
})
.listen(8080);
浏览器上的结果:
Promise.all(array)需要传入一个promise数组,其中数组中的每一个promise在fulfill时都会执行resolve(data),这里的data就是前面for循环中每一次异步操作中获得的数据。在promise.all()执行过后,会将每次resolve(data)中的data拼成一个数组,通过then()传递给下一个promise。
Promise.race()
Promise.race()为异步任务提供了竞争机制。比如在N个异步任务中,在最快获得结果的任务之后做某些事情,可以使用Promise.race()。
使用Promise.race()同Promise.all()类似,传入的参数都是promise数组,返回promise数组中最早fulfill的promise,或者返回最早reject的promise。
function a () {
var promise = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('a');
}, 1002);
});
return promise;
}
function b () {
var promise = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('b');
}, 1001);
});
return promise;
}
function c (data) {
console.log(data + ' first!');
}
Promise.race([a(), b()]).then(c);
执行结果:b first!
JavaScript Promises的更多相关文章
- 异步编程之Javascript Promises 规范介绍
什么是 Promises Promises是一种关于异步编程的规范,目的是将异步处理对象和处理规则进行规范化,为异步编程提供统一接口. 传统的回调函数 说到JavaScript的异步编程处理,通常我们 ...
- Javascript Promises 介绍
什么是 Promises Promises是一种关于异步编程的规范,目的是将异步处理对象和处理规则进行规范化,为异步编程提供统一接口. 传统的回调函数 说到JavaScript的异步编程处理,通常 ...
- JavaScript Promises 实现框架一览及对比
In my previous post in Danish I looked at how to perform asynchronous calls by using promises. Now t ...
- javascript promises powered by BlueBird
什么是promises? 为什么需要promises? 参见: https://promisesaplus.com/ 使用示例: 使用promises之前,代码的编写方式: 使用promises之后: ...
- Javascript Promises学习
Promise对象的三个状态 pending(进行中) fulfilled(已成功) rejected(已失败) Promise代表一个异步操作,对象的状态一旦改变,就不会再改变 Promise构造函 ...
- JavaScript进阶之路——认识和使用Promise,重构你的Js代码
一转眼,这2015年上半年就过去了,差不多一个月没有写博客了,"罪过罪过"啊~~.进入了七月份,也就意味着我们上半年苦逼的单身生活结束了,从此刻起,我们要打起十二分的精神,开始下半 ...
- 【JavaScript】JavaScript Promise 探微
http://www.html-js.com/article/Promise-translation-JavaScript-Promise-devil-details 原文链接:JavaScript ...
- JavaScript周报#183
This week’s JavaScript news Read this issue on the Web | Issue Archive JavaScript Weekly Issue 183Ma ...
- JavaScript Promise启示录--(转)
本博文转至:http://www.csdn.net/article/2014-05-28/2819979-JavaScript-Promise [编者按]JavaScript是一种基于对象和事件驱动并 ...
随机推荐
- VC++ 编译libcurl 支持SSL,GZIP
由于网上下载的 libcurl 不支持 gzip,只好自己动手编译,期间走了很多弯路,下面是最终成功的记录. 我所使用的环境 Visual Studio 2010 . Windows 7 64 bit ...
- 二、springcloud之熔断器hystrix
一.背景 雪崩效应 在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应.服务雪崩效应是一种因“服务提供者”的不可用导致“服 ...
- git —— 异常1,index.lock
git提交过程中出现的问题 解决方法:找到 index.lock文件将其删除 一般 index.lock 在.git下面, 有时 .git 是隐藏的,但是无论怎样, 可以通过 everything 找 ...
- python3 安装
Centos7 安装python3 #安装sqlite-devel yum -y install sqlite-devel #安装依赖 yum -y install make zlib zlib-de ...
- 编写组件TComponent published $M+ 问题
报错如下: PUBLISHED caused RTTI ($M+) to be added to type 修改成下面这样之后: 解决问题 方法: 来自:http://www.cnblogs.com/ ...
- 开启nginx目录文件列表功能
ngx_http_autoindex_module 此模块用于自动生成目录列表,ngx_http_autoindex_module只在 ngx_http_index_module模块未找到索引文件时 ...
- sublime text配置fcitx输入法
sublime text配置fcitx输入法 环境:Fedora20 输入法:fcitx sublime text:version 3 安装fcitx输入法 这个看前面教程 编译sublime-imf ...
- 如何查看K8S的网络是否完好
今天工作中遇到这个问题, 检查从以下几个方面入手. 一,查看各个POD的LOG,如果有错误,则要解决了再继续 二,登陆各个POD之间,互相要能PING通. 三,在物理节点上可以PING通SERVICE ...
- bzoj 1211: [HNOI2004]树的计数
prufer的应用.. 详细见这篇博客:https://www.cnblogs.com/dirge/p/5503289.html import java.math.BigInteger; import ...
- iconfont 在项目中的简单使用
font-class引用 font-class是unicode使用方式的一种变种,主要是解决unicode书写不直观,语意不明确的问题. 与unicode使用方式相比,具有如下特点: 兼容性良好,支持 ...