(翻译)异步编程之Promise(1):初见魅力
by Forbes Lindesay
异步编程系列教程:
- (翻译)异步编程之Promise(1)——初见魅力
- 异步编程之Promise(2):探究原理
- 异步编程之Promise(3):拓展进阶
- 异步编程之Generator(1)——领略魅力
- 异步编程之Generator(2)——剖析特性
- 异步编程之co——源码分析
动机
思考一下,下面这段用来读取文件并解析JSON的Javascript同步代码。它很简单并且易于阅读,但是因为它会阻塞代码,你并不会想用在大多数的应用里。这意味着,当你用它来读取文件的时候(它需要很多时间)不会有其他的事情发生。
function readJSONSync(filename){
// 读取文件后,再解析成JSON
return JSON.parse(fs.readFileSync(filename, 'utf-8'));
}
为了让我们的应用高性能且实时响应,我们需要让所有涉及到IO的操作都变成异步的。最简单的方法去实现它就是使用callback回调。然而,一个幼稚不成熟的代码实现也许会让它出错。
function readJSON(filename, callback){
fs.readFile(filename, 'utf-8', function(err, res){
if(err)
return callback(err);
// 回调参数为,错误null和文件解析后的JSON
callback && callback(null, JSON.parse(res));
})
}
出现的问题:
- 额外的
callback回调参数会使我们困惑,不知道变量到底是输入值还是返回值。 - It doesn't work at all with control flow primitives.(这里我无法理解控制什么流呢?)
- 无法处理由
JSON.parse抛出的错误。
我们需要处理由JSON.parse抛出的错误,但是我们同样也需要小心不要影响到了由callback函数抛出的错误。最后我们用一堆混乱的错误处理完成了:
function readJSON(filename, callback){
fs.readFile(filename, 'utf-8', function(err, res){
if(err)
return callback(err);
try{
res = JSON.parse(res);
} catch(ex){
return callback(ex);
}
callback(null, res);
});
}
尽管有这些杂乱代码来处理错误,我们仍然留下一个问题就是callback烦人的回调参数。Promise可以帮助你更自然的处理错误,没有callback的参数使代码更简洁,并且用不着修改底层的结构(意思是你可以用原生Js来实现promise,并且用它来封装已经存在的异步操作)
什么是promise?
promise背后的核心思想就是,一个promise代表了一个异步操作的结果。一个promise只有三种不同的形态:
- pending - 等待,是promise的初始状态
- fulfilled - 完成,这个promise状态代表着操作成功(有的也称resolve解决)
- rejected - 拒绝,这个promise状态代表了操作失败
一旦promise是fulfilled状态或rejected状态,那么它就是固定不会再改变的了。
构建一个promise
当以后所有的APIs都转变成promises,你应该会特别少机会去手动构建promise。在此期间,我们需要一个方法来转变现有的APIs。举个栗子:
function readFile(filename, encoding){
return new Promise(function(resolve, reject){
fs.readFile(filename, encoding, function(err, res){
if(err)
return reject(err);
resolve(res);
});
});
}
我们使用ES6的new Promise来构建promise。我们给构造器一个生成promise的工厂函数。这个带两个参数的函数会立即调用。第一个参数用来使promise转变成成功状态,第二个参数使promise转变成失败状态。一旦操作完成后,我们将会调用相应合适的函数。
等待一个promise
为了使用promise,我们必须用某种方法去等待promise的状态是成功了还是失败了。这个方法在promise/A里,就是使用promise.then(resolve, reject)。promise.then()会返回promise以提供链式调用。
根据这个,我们可以利用promise轻松的重写之前的readJSON函数:
function readJSON(filename, encoding){
return new Promise(function(resolve, reject){
readFile(filename, encoding).then(function(res){
try{
resolve(JSON.parse(res));
}catch(ex){
reject(ex);
}
}, reject);
});
}
这次,我们是把readJSON转化成新的promise返回出来,提供接下来的使用。
变化/链式结构
通过我们的例子,我们真正希望做到的是让另外的操作也变成promise化。在我们的例子中,第二个操作是同步的(指JSON.parse()),但是readJSON已经简单的转变成一个异步的操作。幸运的是,promise的then()方法可以将其变成链式操作。
现在我们可以更简洁地重写我们原本的例子:
function readJSON(filename, encoding){
return readFile(filename, encoding).then(function(res){
return JSON.parse(res);
});
}
因为JSON.parse仅仅是个函数,我们可以重写成这样:
function readJSON(filename, encoding){
return readFile(filename, encoding).then(JSON.parse);
}
这和我们一开始写的最简单的同步代码已经非常相似了!我认为用链式结构调用,会更符合自然逻辑。
最后实现的代码如下所示:
//data.json文件
//{"message": "Hello World!"}
//readFile的Promise化
function readFile(filename, encoding){
return new Promise(function(resolve, reject){
fs.readFile(filename, encoding, function(err, res){
if(err)
return reject(err);
resolve(res);
});
});
}
//readJSON的Promise化
function readJSON(filename, encoding){
return readFile(filename, encoding).then(JSON.parse);
}
// readJSON函数的使用
readJSON("data.json", 'utf-8').then(function(data){
console.log(data.message); // Hello World!
});
我们可以看到臃肿混乱的回调金字塔已经消失了,剩下的是清爽干净的链式promise。而错误,我们也可以很轻松的进行捕捉处理。
(翻译)异步编程之Promise(1):初见魅力的更多相关文章
- 异步编程之Generator(1)——领略魅力
异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...
- 异步编程之Promise(3):拓展进阶
异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...
- 异步编程之Promise(2):探究原理
异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...
- ECMA Script 6_异步编程之 Promise
Promise 对象 异步编程 方案,已同步的方式表达异步的代码,解决回调地狱的问题 比传统的解决方案——回调函数和事件——更合理和更强大 是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步 ...
- 前端异步编程之Promise和async的用法
传统的异步解决方案采用回调函数和事件监听的方式,而这里主要记录两种异步编程的新方案: ES6的新语法Promise ES2017引入的async函数 Generator函数(略) Promise的含义 ...
- JavaScript的异步编程之Promise
Promise 一种更优的异步编程统一 方法,如果直接使用传统的回调函数去完成复杂操作就会形成回调深渊 // 回调深渊 $.get('/url1'() => { $.get('/url2'() ...
- 异步编程之co——源码分析
异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...
- 异步编程之Generator(2)——剖析特性
异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...
- Javascript异步编程之setTimeout与setInterval详解分析(一)
Javascript异步编程之setTimeout与setInterval 在谈到异步编程时,本人最主要会从以下三个方面来总结异步编程(注意:特别解释:是总结,本人也是菜鸟,所以总结不好的,请各位大牛 ...
随机推荐
- R语言缺失值信息处理
mean(!is.na(mat))可以计算数据完整度(没有缺失值的) mean(!is.na(mat))>0.9,90%完整可以使用 # 缺失值的位置研究as.vector(attributes ...
- Qt之HTTPS登录
简述 HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版.即HTTP ...
- 漫游Kafka设计篇之Producer和Consumer
Kafka Producer 消息发送 producer直接将数据发送到broker的leader(主节点),不需要在多个节点进行分发.为了帮助producer做到这点,所有的Kafka节点都可以及时 ...
- Codeforces 279 B Books
题意:给出n本书,总的时间t,每本书的阅读时间a[i],必须按照顺序来阅读,问最多能够阅读多少本书 有点像紫书的第七章讲的那个滑动区间貌似 维护一个区间的消耗的时间小于等于t,然后维护一个区间的最大值 ...
- UVa 1395 Slim Span【最小生成树】
题意:给出n个节点的图,求最大边减最小边尽量小的值的生成树 首先将边排序,然后枚举边的区间,判定在该区间内是否n个点连通,如果已经连通了,则构成一颗生成树, 则此时的苗条度是这个区间内最小的(和kru ...
- 新浪微博顶部新评论提示层效果——position:fixed
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 重拾Excel之为什么
现在如今想想自己,已经有许久许久没有充过电了.现在想好好地充电. 机遇总是垂青于有准备的人!
- mysql利用存储过程批量插入数据
最近需要测试一下mysql单表数据达到1000W条以上时增删改查的性能.由于没有现成的数据,因此自己构造,本文只是实例,以及简单的介绍. 首先当然是建表: [sql]view plaincopy CR ...
- linux 安装SVN
1.环境centos6.4 2.安装svnyum -y install subversion 3.配置 建立版本库目录mkdir /www/svndata svnserve -d -r /www/sv ...
- 前端程序员:月薪 5K 到 5 万,我干了啥(转)
转自:http://www.imooc.com/article/4110 前端程序员:月薪 5K 到 5 万,我干了啥前端开发工作已经变的越来越复杂,仅仅是想罗列一份前端开发的学习列表就已经是一件艰巨 ...