ES6里的Promise改进是重大的吗?
ES6的细节披露的越来越多了,最近听说里面增加了一个Promise特性,我去翻了下这个关键字,貌似在js上有很多实现了,那么es6的原生实现有什么不同?它的具体作用是什么呢?就是为了解决异步嵌套的问题吗?
Answers
Promise 对象曾经以多种形式存在于很多语言中。这个词最先由C++工程师用在Xanadu 项目中,Xanadu 项目是Web 应用项目的先驱。随后Promise 被用在E编程语言中,这又激发了Python 开发人员的灵感,将它实现成了Twisted 框架的Deferred 对象。2007 年,Promise 赶上了JavaScript 大潮,那时Dojo 框架刚从Twisted框架汲取灵感,新增了一个叫做dojo.Deferred 的对象。
现阶段jquery中就实现了promise对象,其实见到promise对象就不能不提deferred对象,deferred对象是promise对象的超集,它比deferred对象多了一个关键功能就是可以直接触发,因此一般构造者持有deferred对象,使用者持有promise对象。在jquery(1.5+)中使用了很多promise,比如最常用的ajax对象,其返回的就是一个promise对象。
var promise = $.get('/mydata');
promise.done(onSuccess);
promise.fail(onFailure);
promise.always(onAlways);
这样的好处就在于能够进行
封装
,只传递promise对象会使整个程序变得优雅,可维护许多,降低代码重复度
如果想深入了解 异步编程,可以看看这本书
Javascript 异步编程
ES6 的 Promises 是采用了 Promises/A+ 提案的一种实现。你现在能够找到的各种第三方实现,如果是完全兼容了 Promises/A+ 的,那么就和 ES6 的 Promises 是一致的(当然了,第三方实现可能会增加更多的 API,而 ES6 的实现只需要符合 Promises/A+ 提案的最基本要求即可)。
Promises 是一种模式,它能够让异步操作变得看起来更像是同步的,无论是写起来还是读起来都会更愉悦更轻松一些。基本上它是把延续传递风格(Continuation-passing style)的代码,比如:
doSomeAsync("url", function (err, results) {
// 在延续传递的回调函数里处理 err,或者处理 results
});
变成返回值的形式:
var promise = doSomeAsync("url")
这个返回的对象就是一个 Promises 对象,它代表着异步操作的结果:可能是成功的获取到某些数据,也可能是失败返回的状态和信息。在我们获得 Promises 对象的那一刻我们并无法知道具体是什么,但是只要异步操作完成,Promises 对象必然会有一个确定状态(所以才叫 Promises,也就是“许诺,承诺”)。我们甚至不需要去查询这个状态,只需要告诉它要做什么就可以了。
貌似延续传递的毁掉函数也能做到上述这些?没错。但是 Promises 的意义就在于它把未来的结果作为一个对象返回给你,你可以用同步的方式来写代码处理它,比如说作为参数传递,比如说聚合多个 Promises 操作等等。
解决异步嵌套并非 Promises 的目的,而是使用 Promises 后得到的一个副作用——当然是好的副作用。Promises 真正的意义在于集中化处理异常。
什么意思呢?考虑一下同步函数,它的特点是要么返回值,要么抛出异常。返回的值可以继续传递给别的函数做进一步处理,而异常则可以在调用栈的某一层被捕获并处理。我们总认为异步回调的问题在于嵌套过深,然而嵌套只是表象,真正的难点在于你无法让异步回调统一处理返回值或异常,所以才不得不嵌套起来。
Promises 把异步操作的结果变成对象返回出来并可以进一步处理,这就好像同步函数的经典行为,于是才可能有这样的异步处理:
var promise = doSomeAsync("url");
promise
.then(doOneThingAsync)
.then(doAnotherThingAsync)
.then(doFinalThing)
.catch(dealWithAnyErrors);
链式调用的最后一个
.catch
,它可以处理前面三步中抛出的任意异常,这才是 Promises 的精髓:把异步操作同步化,因而不用嵌套回调函数并且能够集中处理异常。
补充一点,jQuery 里实现的 Promises 是有问题的(和 Promises/A+ 比较),而且问题比较严重。如果你希望使用 Promises 的习惯以及相关的 API 符合未来的 ES6,那么请不要视 jQuery 的实现或行为是“正确的”,甚至根据 jQuery 核心成员的描述 ,这些问题将永远得不到解决(在 jQuery 中,出于向后兼容的考虑)。