文档章节

初识JavaScript Promises

梵高
 梵高
发布于 2014/06/29 22:50
字数 1166
阅读 2897
收藏 99
点赞 9
评论 15

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。”

其中,“差强人意”的意思是“大体上令人满意”,用在原文这里不太合适
语文是体育老师教的,不好意思,已经修正
又问我哪个log先输出?我们把promise和eventloop放一起谈谈吧

废话就不多说了,这篇文章就不再聊关于promise的各种好处和用法了,如果不了解请自行Google啦! 我相信很多人在面试的时候遇到过这样一道面试题: 那么你的答案是什么呢? 粘贴到chrome的控制...

beth_miao ⋅ 05/15 ⋅ 0

Modern JS中的流控制:CallBacks->Promises->Async/Await

今天来聊一聊JS中的异步发展,还有推荐的异步调用写法. 单线程模式 JS运行在一个单处理线程上运行。当你操作一个标签时,其他的JS代码就会等待该操作执行完毕。浏览器的DOM操作不会发生在并行...

含笑666 ⋅ 06/06 ⋅ 0

浏览器的event loop和node的event loop

1.什么是event loop event loops也就是事件循环,它是为了协调事件(event),用户交互(user interaction),脚本(script),渲染(rendering),网络(networking),用户代理(user agen...

金大光 ⋅ 05/27 ⋅ 0

学习Javascript的8张思维导图

分别归类为:  javascript变量  javascript运算符  javascript数组  javascript流程语句  javascript字符串函数  javascript函数基础  javascript基础DOM操作  javascript正则表达式...

thinkyoung ⋅ 2014/09/23 ⋅ 0

JavaWeb01-HTML篇笔记(七)

.1 案例三:完成对注册页面的数据的简单校验.1.1.1 需求: 对注册页面的数据进行非空的简单校验!!!如果有某个值没有输入,点击提交,弹出一个对话框进行提示!! 1.1.2 分析:1.1.2.1 技术分...

我是小谷粒 ⋅ 04/28 ⋅ 0

iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge

iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge 转载:原地址 https://www.jianshu.com/p/e951af9e5e74 上一篇文章介绍了UIWebView 如何通过WebViewJavascriptBridge 来实现......

法斗斗 ⋅ 05/11 ⋅ 0

code-rhythm:写了个vscode扩展,让代码更有快感

项目地址 Github - onvno/code-rhythm 原因 写代码本身是件快乐的事情,但开发中总有各种烦恼。 有时候一个很简单的方法,因为不确定传参的形式,不确定返回形式,不确定具体用法,就得翻墙,...

onvno_ ⋅ 06/07 ⋅ 0

爬虫获取 js 动态数据 (1)

爬虫遇到 js 动态数据时,主要解决方法有两种: 使用一些库,例如 Selenium,来模拟浏览器环境抓取数据。但这样做对内存和 CPU 的消耗都比较大,爬虫效率低,应尽量避免。 手动分析 js 请求,...

anye137 ⋅ 06/05 ⋅ 0

WebAssembly 时代,Rust 也想成为 Web 语言

目前 Mozilla 正在基于 WebAssembly 可移植代码格式研发 JavaScript 和 Rust 之间的桥梁——wasm-bindgen,意义是提高 JavaScript 和 Rust 之间的互操作性。Mozilla 这么做是想让 Rust 成为类...

开源中国 ⋅ 04/10 ⋅ 0

Lynx技术分析-JS引擎扩展设计

JS Binding 技术 Lynx(一个高效的跨平台框架) 的 JS Binding 技术最主要的目的是搭建一个高效的与 JS 引擎解耦的通信桥梁,同时具备 JS 引擎切换的能力。该技术经历了多次迭代,最终通过抽...

hxxft ⋅ 05/15 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Centos7重置Mysql 8.0.1 root 密码

问题产生背景: 安装完 最新版的 mysql8.0.1后忘记了密码,向重置root密码;找了网上好多资料都不尽相同,根据自己的问题总结如下: 第一步:修改配置文件免密码登录mysql vim /etc/my.cnf 1...

豆花饭烧土豆 ⋅ 今天 ⋅ 0

熊掌号收录比例对于网站原创数据排名的影响[图]

从去年下半年开始,我在写博客了,因为我觉得业余写写博客也还是很不错的,但是从2017年下半年开始,百度已经推出了原创保护功能和熊掌号平台,为此,我也提交了不少以前的老数据,而这些历史...

原创小博客 ⋅ 今天 ⋅ 0

LVM讲解、磁盘故障小案例

LVM LVM就是动态卷管理,可以将多个硬盘和硬盘分区做成一个逻辑卷,并把这个逻辑卷作为一个整体来统一管理,动态对分区进行扩缩空间大小,安全快捷方便管理。 1.新建分区,更改类型为8e 即L...

蛋黄Yolks ⋅ 今天 ⋅ 0

Hadoop Yarn调度器的选择和使用

一、引言 Yarn在Hadoop的生态系统中担任了资源管理和任务调度的角色。在讨论其构造器之前先简单了解一下Yarn的架构。 上图是Yarn的基本架构,其中ResourceManager是整个架构的核心组件,它负...

p柯西 ⋅ 今天 ⋅ 0

uWSGI + Django @ Ubuntu

创建 Django App Project 创建后, 可以看到路径下有一个wsgi.py的问题 uWSGI运行 直接命令行运行 利用如下命令, 可直接访问 uwsgi --http :8080 --wsgi-file dj/wsgi.py 配置文件 & 运行 [u...

袁祾 ⋅ 今天 ⋅ 0

JVM堆的理解

在JVM中,我们经常提到的就是堆了,堆确实很重要,其实,除了堆之外,还有几个重要的模块,看下图: 大 多数情况下,我们并不需要关心JVM的底层,但是如果了解它的话,对于我们系统调优是非常...

不羁之后 ⋅ 昨天 ⋅ 0

推荐:并发情况下:Java HashMap 形成死循环的原因

在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障,并且这个事发生了很多次,原因是在Java语言在并发情况下使用HashMap造成Race Condition,从而导致死循环。这个事情我4、5年前也经历...

码代码的小司机 ⋅ 昨天 ⋅ 2

聊聊spring cloud gateway的RetryGatewayFilter

序 本文主要研究一下spring cloud gateway的RetryGatewayFilter GatewayAutoConfiguration spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/config/G......

go4it ⋅ 昨天 ⋅ 0

创建新用户和授予MySQL中的权限教程

导读 MySQL是一个开源数据库管理软件,可帮助用户存储,组织和以后检索数据。 它有多种选项来授予特定用户在表和数据库中的细微的权限 - 本教程将简要介绍一些选项。 如何创建新用户 在MySQL...

问题终结者 ⋅ 昨天 ⋅ 0

android -------- 颜色的半透明效果配置

最近有朋友问我 Android 背景颜色的半透明效果配置,我网上看资料,总结了一下, 开发中也是常常遇到的,所以来写篇博客 常用的颜色值格式有: RGB ARGB RRGGBB AARRGGBB 这4种 透明度 透明度...

切切歆语 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部