文档章节

初识JavaScript Promises

梵高
 梵高
发布于 2014/06/29 22:50
字数 1166
阅读 2904
收藏 98

JavaScript有很多槽点,嵌套回调怕是千夫所指。

很久之前,我一直使用async来处理JavaScript异步编程中的嵌套回调问题。当然我也大概的了解过一些其它旨在解决这些问题的类库,诸如EventProxy、Jscex、StepJS、thenjs。

当我第一次看到Promises规范的时候,我根本无法理解它所带来的好处。譬如每个初次学习Promises的人都见过如下的示例代码:

//callbacks 
function callback(err, value){ 	
    if(err){ 		
        // do something 		
        return; 	
    } 	
    //do other things     with value 
} 
//Promises 
promise.then(function(value){ 	
    //do something with value 
}, function(err){ 	
    //do other things with error 
})

很难相信上面的代码会让人对Promises刮目相看。不过正如bluebird作者Petka所说,上面的代码是 “最不诚实的比较”。所以我恳请你把类似的代码从你的记忆中擦出吧。

不妨让我们再回到async的讨论上。async的问题在于它不能优雅地应对需求的变化,一旦业务逻辑有较大的变化,代码结构会进行大幅度的调整,而Promises却能够轻松的应对这种变化。待时机适宜我会进行详细的比较,首先让我们开始快速地了解Promises。


Promises是什么

Promises象征着一个异步操作的最终结果。Promises交互主要通过它的then方法,then方法接受一个回调函数,这个回调函数接受执行成功的返回值或执行失败的错误原因,错误原因一般是Error对象。需要注意的是,then方法执行的返回值是一个Promise对象,而then方法接受的回调函数的返回值则可以是任意的JavaScript对象,包括Promises。基于这种机制,Promise对象的链式调用就起作用了。

Promises的状态

Promise对象有三种状态:pending(初始状态)、fulfilled(成功执行)、rejected(执行出错)。pending状态的Promise对象可以转换到其它两种状态。


上面的文本不够形象,不妨上些代码来加深对Promises的认识。

注:由于主流的JavaScript环境(包括NodeJS)对Promises/A+标准的实现不太令人满意,我的示例均使用了第三方类库bluebird

var fs = require('fs')
var Promise = require('bluebird')
//改造fs.readFile为Promise版本
var readFileAsync = function(path){
    //返回一个Promise对象,初始状态pending
    return new Promise(function(fulfill, reject){
	fs.readFile(path,  'utf8', function(err, content){
		//由pending状态进入rejected状态
		if(err)return reject(err)
		//由pending状态进入fulfilled状态
		return fulfill(content)
	})
})
}

//开始使用,调用其then方法,回调接受执行成功的返回值
readFileAsync('./promise-1.js').then(function(content){
console.log(content)
})

看了上面的代码以后,是不是觉得Promises其实并不复杂呢。

OK,我们继续延续上面的代码,来简单比较一下传统回调和Promises的使用上的差别: /* * 简单比较一下传统方式和Promises方式 * 需求:读取两个文件并打印内容 * */

 //callbacks
fs.readFile('./promise-1.js', 'utf8', function(err, content1){
	//嵌套一次
	console.log('#', content1)
	fs.readFile('./promise-1.js', 'utf8', function(err, content2){
 		//第二次嵌套
		console.log('##', content2)
	})
})

//Promises
readFileAsync('./promise-1.js').then(function(content1){
	console.log('#', content1)
	//这里返回一个Promise对象
	return readFileAsync('./promiscuitye-1.js')
}).then(function(content2){
	console.log('##', content2)
})

上面的代码都没有错误处理,这是一个后果很严重的坏习惯。不过今天我们的重点不在这里,而是分析上下两段代码的主要区别。

第一段代码是传统的嵌套回调,在第二次打印的时候已经使用了两次缩进,而Promises链式调用then方法成功地避免了一次缩进(嵌套),维持了代码结构的相对平坦。上面的代码略显简陋,如果再加上错误处理,Promises毫无疑问将会大放光彩,有兴趣请关注后续章节。

本章写到这里就结束了,相信大家已经对Promises的有了一个初步认识。规范文档往往很难理解,我没有过多的描述规范,因为我相信代码最能够解释一切。不过对规范文档有兴趣的可以自行阅读参考链接。

最后我想强调的一点就是:Promises这种维持代码结构平坦的魔力在业务逻辑复杂多变的情况下是非常有用的

参考链接


未完待续(2014-06-28 00:59)

© 著作权归作者所有

共有 人打赏支持
梵高
粉丝 21
博文 20
码字总数 6905
作品 0
深圳
程序员
加载中

评论(15)

梵高
梵高

引用来自“luwenhua”的评论

readFileAsync('./promise-1.js').then(function(content1){
console.log('#', content1)
//这里返回一个Promise对象
return readFileAsync('./promiscuitye-1.js')
}).then(function(content2){
console.log('##', content2)
})
请问,看上去第一个读文件会阻塞第二个,是这样么?
其实这里谈不上阻塞,只是例子是先读取第一个文件然后读取第二个文件。 只要不使用同步方法,是不会阻塞的。
luwenhua
luwenhua
readFileAsync('./promise-1.js').then(function(content1){
console.log('#', content1)
//这里返回一个Promise对象
return readFileAsync('./promiscuitye-1.js')
}).then(function(content2){
console.log('##', content2)
})
请问,看上去第一个读文件会阻塞第二个,是这样么?
梵高
梵高

引用来自“撸蕉香的程猿序”的评论

//开始使用,调用其then方法,回调接受执行成功的返回值

readFileAsync('./promise-1.js').then(function(content){

console.log(content)

如何接受失败的回调函数呢
下一篇会讲到。后面计划还有三到五篇
码农与厨子
码农与厨子
//开始使用,调用其then方法,回调接受执行成功的返回值

readFileAsync('./promise-1.js').then(function(content){

console.log(content)

如何接受失败的回调函数呢
梵高
梵高

引用来自“webit”的评论

首先抱歉,我只看了你的文章的第一句话:“JavaScript有很多槽点,嵌套回调怕是千夫所指。”我不明白这两句有啥关系? 语言本身确实是“有很多槽点”,但是“嵌套回调”本身这个特性没问题吧? 就像一个工具,用得好不好更多的得看是谁用
嵌套回调被吐槽已经是不争的事实了吧。
梵高
梵高

引用来自“Angry_Snail”的评论

嵌套回调导致的代码可读性、可维护性、及变量作用域问题,谁用谁知道
对于Python程序员来说缩进超过5个就是耻辱!!

前端 AngularJS + $q
服务器 NodeJS + q

以上,不谢
Angry_Snail
Angry_Snail
嵌套回调导致的代码可读性、可维护性、及变量作用域问题,谁用谁知道
对于Python程序员来说缩进超过5个就是耻辱!!

前端 AngularJS + $q
服务器 NodeJS + q

以上,不谢
zqq90
zqq90
首先抱歉,我只看了你的文章的第一句话:“JavaScript有很多槽点,嵌套回调怕是千夫所指。”我不明白这两句有啥关系? 语言本身确实是“有很多槽点”,但是“嵌套回调”本身这个特性没问题吧? 就像一个工具,用得好不好更多的得看是谁用
灰花走湿
灰花走湿
https://www.promisejs.org/
梵高
梵高

引用来自“大头先生”的评论

提个文字用语的建议:

文中关于“Promise的状态”部分 -
“由于主流的JavaScript环境(包括NodeJS)对Promises/A+标准的实现差强人意,我的示例均使用了第三方类库bluebird。”

其中,“差强人意”的意思是“大体上令人满意”,用在原文这里不太合适
语文是体育老师教的,不好意思,已经修正
初识JavaScript Promises之二

初始JavaScript Promises之二 上一篇我们初步学习了JavaScript Promises,本篇将介绍Promise如何优雅地进行错误处理以及提升操作node.js风格1的异步方法的逼格,没错就是使用promisify2。 异...

梵高
2014/07/15
0
0
JavaScript一团乱,这是好事

译者按: JavaScript从简单变复杂了,作者从另一个角度看待这个问题。 原文: JavaScript’s a mess – and that’s a good thing 译者: Fundebug 为了保证可读性,本文采用意译而非直译。另外...

Fundebug
07/03
0
0
JavaScript 一团乱,这是好事

译者按: JavaScript从简单变复杂了,作者从另一个角度看待这个问题。 原文: JavaScript’s a mess – and that’s a good thing 译者: Fundebug 为了保证可读性,本文采用意译而非直译。另外...

a独家记忆
07/18
0
0
异步JavaScript与Promise

异步? 我在很多地方都看到过异步(Asynchronous)这个词,但在我还不是很理解这个概念的时候,却发现自己常常会被当做“已经很清楚”(* ̄ロ ̄)。 如果你也有类似的情况,没关系,搜索一下这个...

银月光海
2015/02/02
0
0
升级:myeclipse 2015 ci 16发布

Slack Integration 新版本集成了Slack,你只需要注册一个Slack帐号然后就可以发送和接收代码片段。你甚至不需要登录Slack就可以直接在Eclipse中查看分享的代码。 Gerrit Tools 如果你用Gerri...

百mumu
2015/10/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

C++ std::thread

C++11提供了std::thread类来表示一个多线程对象。 1,首先介绍一下std::this_thread命名空间: (1)std::this_thread::get_id():返回当前线程id (2)std::this_thread::yield():用户接口...

yepanl
52分钟前
2
0
Nignx缓存文件与动态文件自动均衡的配置

下面这段nginx的配置脚本的作用是,自动判断是否存在缓存文件,如果有优先输出缓存文件,不经过php,如果没有,则回到php去处理,同时生成缓存文件。 PHP框架是ThinkPHP,最后一个rewrite有关...

swingcoder
56分钟前
1
0
20180920 usermod命令与用户密码管理

命令 usermod usermod 命令的选项和 useradd 差不多。 一个用户可以属于多个组,但是gid只有一个;除了gid,其他的组(groups)叫做扩展组。 usermod -u 1010 username # 更改用户idusermod ...

野雪球
57分钟前
1
0
Java网络编程基础

1. 简单了解网络通信协议TCP/IP网络模型相关名词 应用层(HTTP,FTP,DNS等) 传输层(TCP,UDP) 网络层(IP,ICMP等) 链路层(驱动程序,接口等) 链路层:用于定义物理传输通道,通常是对...

江左煤郎
今天
1
0
使用xtrabackup完成远程备份

转载收藏,以防丢失 需求 Can I backup remote databases from my local server02-27-2013, 06:17 AMHi, I am using mysqldump so far for taking daily backups of my Production datab......

阿dai
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部