promise 参数.resolve能接收多个参数吗

JavaScript Promise启示录
发表于 09:31|
来源alloyteam|
作者TAT.dmyang
摘要:目前高级浏览器如Chrome、Firefox都已经内置了Promise对象,提供更多的操作接口,如此优雅的Promise具备哪些特性呢?作者TAT.dmyang就JavaScript中的Promise规范给出了一些见解,一起来看下。
【编者按】JavaScript是一种基于对象和事件驱动并具有相对安全性的客户端脚本语言。自推出后就大受开发者的青睐,基于也不计其数,开发者们可以灵活选择,轻松构建应用。原文作者TAT.dmyang就JavaScript中的Promise规范给出了一些见解,目前高级浏览器如Chrome、Firefox都已经内置了Promise对象,提供更多的操作接口,如此优雅的Promise具备哪些特性呢?且看下文:一直以来,JavaScript处理异步都是以callback的方式,在前端开发领域callback机制几乎深入人心。在设计API的时候,不管是浏览器厂商还是SDK开发商亦或是各种类库的作者,基本上都已经遵循着callback的套路。
近几年随着JavaScript开发模式的逐渐成熟,CommonJS规范顺势而生,其中就包括提出了Promise规范,Promise完全改变了js异步编程的写法,让异步编程变得十分的易于理解。
在callback的模型里边,我们假设需要执行一个异步队列,代码看起来可能像这样:
loadImg('a.jpg', function() {
loadImg('b.jpg', function() {
loadImg('c.jpg', function() {
console.log('all done!');
这也就是我们常说的回调金字塔,当异步的任务很多的时候,维护大量的callback将是一场灾难。当今Node.js大热,好像很多团队都要用它来做点东西以沾沾“洋气”,曾经跟一个运维的同学聊天,他们也是打算使用Node.js做一些事情,可是一想到js的层层回调就望而却步。
好,扯淡完毕,下面进入正题。
Promise可能大家都不陌生,因为Promise规范已经出来好一段时间了,同时Promise也已经纳入了ES6,而且高版本的chrome、firefox浏览器都已经原生实现了Promise,只不过和现如今流行的类Promise类库相比少些API。
所谓Promise,字面上可以理解为“承诺”,就是说A调用B,B返回一个“承诺”给A,然后A就可以在写计划的时候这么写:当B返回结果给我的时候,A执行方案S1,反之如果B因为什么原因没有给到A想要的结果,那么A执行应急方案S2,这样一来,所有的潜在风险都在A的可控范围之内了。
上面这句话,翻译成代码类似:
var resB = B();
var runA = function() {
resB.then(execS1, execS2);
只看上面这行代码,好像看不出什么特别之处。但现实情况可能比这个复杂许多,A要完成一件事,可能要依赖不止B一个人的响应,可能需要同时向多个人询问,当收到所有的应答之后再执行下一步的方案。最终翻译成代码可能像这样:
var resB = B();
var resC = C();
var runA = function() {
.then(resC, execS2)
.then(resD, execS3)
.then(resE, execS4)
.then(execS1);
在这里,当每一个被询问者做出不符合预期的应答时都用了不同的处理机制。事实上,Promise规范没有要求这样做,你甚至可以不做任何的处理(即不传入then的第二个参数)或者统一处理。
好了,下面我们来认识下:
一个promise可能有三种状态:等待(pending)、已完成(fulfilled)、已拒绝(rejected)
一个promise的状态只可能从“等待”转到“完成”态或者“拒绝”态,不能逆向转换,同时“完成”态和“拒绝”态不能相互转换
promise必须实现then方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致
then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用。同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。
可以看到,Promise规范的内容并不算多,大家可以试着自己实现以下Promise。
以下是笔者自己在参考许多类Promise库之后简单实现的一个Promise,代码请移步。
简单分析下思路:
构造函数Promise接受一个函数resolver,可以理解为传入一个异步任务,resolver接受两个参数,一个是成功时的回调,一个是失败时的回调,这两参数和通过then传入的参数是对等的。
其次是then的实现,由于Promise要求then必须返回一个promise,所以在then调用的时候会新生成一个promise,挂在当前promise的_next上,同一个promise多次调用都只会返回之前生成的_next。
由于then方法接受的两个参数都是可选的,而且类型也没限制,可以是函数,也可以是一个具体的值,还可以是另一个promise。下面是then的具体实现:
Promise.prototype.then = function(resolve, reject) {
var next = this._next || (this._next = Promise());
var status = this.
if('pending' === status) {
isFn(resolve) && this._resolves.push(resolve);
isFn(reject) && this._rejects.push(reject);
if('resolved' === status) {
if(!isFn(resolve)) {
next.resolve(resolve);
x = resolve(this.value);
resolveX(next, x);
} catch(e) {
this.reject(e);
if('rejected' === status) {
if(!isFn(reject)) {
next.reject(reject);
x = reject(this.reason);
resolveX(next, x);
} catch(e) {
this.reject(e);
这里,then做了简化,其他promise类库的实现比这个要复杂得多,同时功能也更多,比如还有第三个参数——notify,表示promise当前的进度,这在设计文件上传等时很有用。对then的各种参数的处理是最复杂的部分,有兴趣的同学可以参看其他类Promise库的实现。
在then的基础上,应该还需要至少两个方法,分别是完成promise的状态从pending到resolved或rejected的转换,同时执行相应的回调队列,即resolve()和reject()方法。
到此,一个简单的promise就设计完成了,下面简单实现下两个promise化的函数:
function sleep(ms) {
return function(v) {
var p = Promise();
setTimeout(function() {
p.resolve(v);
function getImg(url) {
var p = Promise();
var img = new Image();
img.onload = function() {
p.resolve(this);
img.onerror = function(err) {
p.reject(err);
由于Promise构造函数接受一个异步任务作为参数,所以getImg还可以这样调用:
function getImg(url) {
return Promise(function(resolve, reject) {
var img = new Image();
img.onload = function() {
resolve(this);
img.onerror = function(err) {
reject(err);
接下来(见证奇迹的时刻),假设有一个BT的需求要这么实现:异步获取一个,解析json数据拿到里边的图片,然后按顺序队列加载图片,没张图片加载时给个loading效果
function addImg(img) {
$('#list').find('& li:last-child').html('').append(img);
function prepend() {
$('&li&')
.html('loading...')
.appendTo($('#list'));
function run() {
$('#done').hide();
getData('map.json')
.then(function(data) {
$('h4').html(data.name);
return data.list.reduce(function(promise, item) {
return promise
.then(prepend)
.then(sleep(1000))
.then(function() {
return getImg(item.url);
.then(addImg);
}, Promise.resolve());
.then(sleep(300))
.then(function() {
$('#done').show();
$('#run').on('click', run);
这里的sleep只是为了看效果加的,可猛击查看!当然,Node.js的例子可查看。
在这里,Promise.resolve(v)静态方法只是简单返回一个以v为肯定结果的promise,v可不传入,也可以是一个函数或者是一个包含then方法的对象或函数(即thenable)。
类似的静态方法还有Promise.cast(promise),生成一个以promise为肯定结果的promise;
Promise.reject(reason),生成一个以reason为否定结果的promise。
我们实际的使用场景可能很复杂,往往需要多个异步的任务穿插执行,并行或者串行同在。这时候,可以对Promise进行各种扩展,比如实现Promise.all(),接受promises队列并等待他们完成再继续,再比如Promise.any(),promises队列中有任何一个处于完成态时即触发下一步操作。
标准的Promise
可参考html5rocks的这篇文章,目前高级浏览器如Chrome、Firefox都已经内置了Promise对象,提供更多的操作接口,比如Promise.all(),支持传入一个promises数组,当所有promises都完成时执行then,还有就是更加友好强大的异常捕获,应对日常的异步编程,应该足够了。
第三方库的Promise
现今流行的各大js库,几乎都不同程度的实现了Promise,如dojo,jQuery、Zepto、when.js、Q等,只是暴露出来的大都是Deferred对象,以jQuery(Zepto类似)为例,实现上面的getImg():
function getImg(url) {
var def = $.Deferred();
var img = new Image();
img.onload = function() {
def.resolve(this);
img.onerror = function(err) {
def.reject(err);
return def.promise();
当然,jQuery中,很多的操作都返回的是Deferred或promise,如animate、ajax:
// animate
$('.box')
.animate({'opacity': 0}, 1000)
.promise()
.then(function() {
console.log('done');
$.ajax(options).then(success, fail);
$.ajax(options).done(success).fail(fail);
// ajax queue
$.when($.ajax(options1), $.ajax(options2))
.then(function() {
console.log('all done.');
}, function() {
console.error('There something wrong.');
jQuery还实现了done()和fail()方法,其实都是then方法的shortcut。
处理promises队列,jQuery实现的是$.when()方法,用法和Promise.all()类似。
其他类库,这里值得一提的是,本身代码不多,完整实现Promise,同时支持browser和Node.js,而且提供更加丰富的API,是个不错的选择。这里限于篇幅,不再展开。
我们看到,不管Promise实现怎么复杂,但是它的用法却很简单,组织的代码很清晰,从此不用再受callback的折磨了。
最后,Promise是如此的优雅!但Promise也只是解决了回调的深层嵌套的问题,真正简化JavaScript异步编程的还是Generator,在Node.js端,建议考虑Generator。参考文献
原文出自:
(责编/夏梦竹)
推荐阅读相关主题:
CSDN官方微信
扫描二维码,向CSDN吐槽
微信号:CSDNnews
相关热门文章AngularJs(2)
一.Promise
& & & & Promise是一个接口,它用来处理的对象具有这样的特点:在未来某一时刻(主要是异步调用)会从服务端返回或者被填充属性。其核心是,promise是一个带有then()函数的对象。
& & & & 为了展示它的优点,下面来看一个例子,其中需要获取用户当前的配置文件:
var&currentProfile&=&null;&&
var&username&=&'something';&&
fetchServerConfig(function(serverConfig)&{&&
&&&&fetchUserProfiles(serverConfig.USER_PROFILES,&username,&&
&&&&&&&&&function(profiles)&{&&
&&&&&&&&&&&&&currentProfile&=&profiles.currentP&&
&&&&&});&&
& & & & 上面这种处理方式存在一些问题:
1.对于代码缩进来说,这种代码就是一个噩梦,尤其在你需要链式调用很多次的时候;
2.处于回调和函数之间的错误报告非常容易丢失,除非你在每一个步骤中都手动处理错误;
3.如果需要使用currentProfile对象来做一些事情,那么你需要在最内层的回调中封装真正想要实现的逻辑,要么直接封装,要么通过一个单独的函数来封装。
& & & & Promise机制可以很好地解决这些问题。在深入了解其运行机制之前,我们来看看如何使用promise实现同样的事情:
var&currentProfile&=&&&
&&&&fetchServerConfig().then(function(serverConfig)&{&&
&&&&&&&return&fetchUserProfiles(serverConfig.USER_PROFILES,&username);&&
&&&&}).then(function(profiles)&{&&
&&&&&&&return&profiles.currentP&&
&&&&},function(error)&{&&
& & & & 使用promise机制的优点如下:
1.可以对函数进行链式调用,所以你不会陷入代码缩进噩梦中;
2.在调用链的过程中,可以保证上一个函数调用完成之后才会调用下一个函数;
3.每一个then()调用都带有两个参数(两个都是函数)。第一个是成功之后的回调,第二个是出错之后的处理器;
4.如果调用链中出现了错误,错误将会被冒泡传递到其余的错误处理函数中。所以,最终来说,所有错误都可以在任意一个回调函数中进行处理。
& & & & 你可能会问,resolve(解决)方法和reject(拒绝)方法又是什么呢?在AngularJS中,延迟调用是实现promise的一种方式。调用resolve方法将会填充promise(也就是调用success处理函数),而调用reject方法将会调用promise的错误处理函数。
二.$q和Promise
& & & & Promise接口是AngularJS组织API的基础,从根本上讲,Promise接口从以下方面对异步请求做了规范:
& & & & a.异步请求返回一个promise,而不是返回具体值;
& & & & b.Promise带有一个then函数,这个函数有两个参数:第一个参数是处理&resolved&和&sucess&事件的函数;第二个参数是处理&rejected&和&failure&事件的函数。调用这两个函数时将会把结果或者拒绝的原因作为参数传递进去;
& & & & c.只要返回结果是合法的,接口就可以保证这两个函数中的一个会被调用。
& & & & 大多数deferred/Q实现都会遵守以上方式,但是AngularJS的实现比较特殊,原因如下:
& & & & a.AngularJS知道$q的存在,所以$q会被整合到作用域模型中去。这样可以使解析时的传递速度更快,并且可以减少UI的闪烁和刷新;
& & & & b.AngularJS的模板也认识$q,这样一来,接口的内容就可以被当作最终结果值(而不是当作promise)来对待,然后等获取结果之后再通知promise;
& & & & c.体积更小,因为对于常用的异步任务来说,AngularJS只实现了它们所需要的最基本、最重要的功能。
& & & & 我们来看一段代码:
fetchUser(function(user)&{&&
&&&&fetchUserPermissions(user,&function(permissions)&{&&
&&&&&&&&fetchUserListData(user,&permissions,&function(list)&{&&
&&&&&&&&&&&&&&
&&&&&&&&});&&
& & & & 使用JavaScript时,人们经常会抱怨这种可怕的、金字塔的代码缩进噩梦。从本质上来说,异步返回的方式和程序的同步处理之间存在冲突,从而导致了多重嵌套的函数,这样就更难跟踪到当前的上下文了。
& & & & 另外,对错误的处理也存在同样的问题。处理错误的最佳方式是什么?你会在每一个步骤中都处理错误吗?那样会把代码搞得一团糟。
& & & & 为了解决这一问题,Promise方案提供了then的概念,在成功的情况下会执行一个函数,在出错的情况下执行另一个函数,两个函数都可以进行链式调用。所以,对于上面这段代码,如果使用Promise API(至少使用AngularJS的实现),可以这样展开:
var&deferred&=&$q.defer();&&
var&fetchUser&=&function()&{&&
&&&&&deferred.resolve(user);&&
&&&&&deferred.reject('Reason&for&failure');&&
deferred.promise.then(fetchUser)&&
&&&.then(fetchUserPermissions)&&
&&&.then(fetchUserListData)&&
&&&.then(function(list)&{&&
&&&&&&&&&&
&&&},function(errorReason(&{&&
& & & & 整个金字塔式的代码就被很好的平坦化了,并且提供了链式的作用域,以及一个单一的出错处理点。你可以在应用中使用同样的代码来处理异步调用,只要导入AngularJS的$q服务即可。
三.拦截响应
& & & & Promise机制还可以做一些非常酷的事情:拦截响应。
& & & & 我们已经学过的内容有:向服务端发送请求、处理响应、把响应很好地包装成抽象的东西及处理异步调用。但是在真实的应用中,对于每一次服务端调用,最终还必须做一些通用的操作,例如错误处理、鉴权以及其他安全方面的处理(例如剪裁数据)。
& & & & 在深入理解了$q接口之后,我们就可以使用拦截响应的方式来处理以上所有任务了。响应拦截的机制允许我们在响应到达应用之前对其进行拦截,并在上面进行一些操作,例如转换数据形式、处理错误等所有你能想到的操作。
& & & & 下面来看一个例子,它会拦截响应,然后做一些很小的数据转换操作。
myModule.factory('myInterceptor',&function($q,&notifyService,&errorLog)&{&&
&&&&&return&function(promise)&{&&
&&&&&&&&&&return&promise.then(function(response)&{&&
&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&return&&&
&&&&&&&&&&},&function(response)&{&&
&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&notifyService(response);&&
&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&errorLog(response);&&
&&&&&&&&&&&&&&&return&$q.reject(response);&&
&&&&&&&&&&});&&
$httpProvider.responseInterceptors.push('myInterceptor');&&
资料来源:《用AngularJS开发下一代Web应用》
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1615次
排名:千里之外}

我要回帖

更多关于 webpack resolve参数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信