You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
在第一次实现的时候,Promise是为了拯救“Pyramid of Doom”: the situation where code marches to the right faster
than it marches forward.
step1(function(value1){step2(function(value2){step3(function(value3){step4(function(value4){// Do something with value4})})})
有了promise库,你就可以压平金字塔了。
Q.fcall(promisedStep1).then(promisedStep2).then(promisedStep3).then(promisedStep4).then(function(value4){// Do something with value4}).catch(function(error){// Handle any error from all above steps}).done();
returngetUsername().then(function(username){returngetUser(username).then(function(user){// if we get here without an error,the value returned here// or the exception thrown here,resolves the promise returned by the first line })});
returngetUsername().then(function(username){returngetUser(username);}).then(function(user){// if we get here without an error,the value returned here// or the exception thrown here,resolves the promise returned by the first line });
functionauthenticate(){returngetUsername().then(function(username){returngetUser(username);})// chained because we will not need the user name in the next event.then(function(user){returngetPassword()// nested because we need both user and password next.then(function(password){if(user.passwordHash!==hash(password)){thrownewError("Can't authenticate");}});});}
returnuploadFile().then(function(){// Success uploading the file}.function(err){// There was an error,and we get the reason for error},function(progress){// We get notified of the upload's progress as it is executed });
就像fail,Q提供了进度回调的缩写progress:
returnuploadFile().progress(function(progress){// We get notified of the upload's progress});
// this:deferred.reject(newError("Can't do it"));// is shorthand for:varrejection=Q.fcall(function(){thrownnewError("Can't do it");});deferred.resolve("rejection");
requestOkText("http://localhost:3000").then(function(responseText){// If the HTTP response return 200 OK,log the response textconsole.log(responseText);},function(error){// If there's an error or a non-200 status code,log the errorconsole.log(error);},function(progress){// Log the progress as it comes inconsole.log("Request progress:"+Math.round(progress*100)+"%");});
Q.js【译文】
* Combination
* Handling Errors
如果一个
function
没有返回一个值,或者没有抛出一个异常阻塞,它就可以返回一个promise。Promise是一个对象,表示返回值或者是函数最终提供的的抛出的异常。Promise也可以作为一个远程对象的代理来克服延迟问题。
在第一次实现的时候,Promise是为了拯救“Pyramid of Doom”: the situation where code marches to the right faster
than it marches forward.
有了promise库,你就可以压平金字塔了。
运用promise,你也能得到通过隐式传播的错误,就像
try
,catch
,finally
。在promisedStep1
产生的错误就会一路传向catch
函数,在catch
里面就可以捕获到这个错误,并且可以对它做出相应的处理。(这里的'promisedStpeN'只是
stepN
的另一个形式,它会返回一个promise。)回调方法称为“控制反转”,一个函数接收一个回调而不是返回一个值,此函数的意思是:“别找我,我会找你的了”。
Promise不会反转倒转,
清晰地分离从控制流参数组输入的参数。
这简化了用法和创建了API,侧重体现在variadic、rest和spread arguments。
Getting Start
Q可以用以下方式加载:
<script>
标签(创建一个Q的全局变量):简化和压缩后 ~2.5KBmicrojs/q
q#1.0.1
Q能够和 jQuery, Dojo, When.js, WinJS,等等混合使用。
Resources
wiki包含了很多有用的资源,包括:
$.Deferred
的指南我们非常欢迎你加入我们的网上论坛
Tutorial
Promise拥有
then
方法:可以用来获取程序返回的值——fulfillment,或者是捕获程序抛出的异常——rejection。如果
promiseMeSomething
函数返回一个promise对象是程序的返回值,那么第一个函数(the fulfillment handler)就会访问这个值。但是如果promiseMeSomething
函数得到的是一个程序抛出的异常,那么第二个函数(the rejection handler)就会对这个异常进行相应处理。需要注意的是:promise的解析是针对异步机制的。也就是说,成功或者失败处理程序将会在下一个事件循环中被调用(即Node中的
process.nextTick
)。这给我们在跟踪代码流的时候一个很好地保障。换句话说,then
将总是在任何处理程序执行之前返回。在本教程中,我们将从如何使用、运用promise进行开发开始。我们将会讲解如何去创建它们,创建一个类似'promiseMeSomething'会返回一个promise对象的函数。
Propagation
then
方法返回一个promise对象,在下面的这个例子中,我们设为'outputPromise'outputPromise
变量将在程序结束前作为一个promise对象,返回一个值。此时函数只能返回一个值或者抛出一个异常。只有一个处理程序将会被调用,它将负责处理'outputPromise'。如果
getInputPromise()
promise结果失败,并且你省略了对于rejection的处理程序,那么error就会返回到outputPromise
:如果输入的promise得到一个成功的结果,但是你省略了对于fulfillment 的处理程序,那么得到的值就会返回到'outputPromise':
当你只关注于对程序抛出的异常的处理的时候,Q promise提供了一个
fail
方法,可以取代then
。如果你只为现代引擎编写JavaScript或者使用CoffeeScript,你就可以使用
catch
来替代fail
。Promise 还有一个
fin
方法,类似于finally
。当promise通过getInputPromise()
在返回一个值或者抛出一个异常之前返回的时候,final处理程序就会在无参的情况下被调用。函数返回的值或是抛出的异常通过getInputPromise()
直接传递到outputPromise
,除非最终处理程序失败,也有可能会延迟,如果final程序返回一个promise。outputPromise
outputPromise
就会被延迟。最终返回的值或是抛出的异常具有和立即返回值或是抛出异常相同的作用:返回的值就会被忽略,抛出的异常就会被传递。如果你只为现代引擎编写JavaScript或者使用CoffeeScript,你就可以使用
finally
来替代fin
。Chaining
Promise的链有两种形式,可以通过内部或者外部处理程序,下面两个例子是等价的。
可以看出唯一的区别就是嵌套的不同。如果你需要在闭包中捕获多个输入值,嵌套处理程序是非常有利于开发的。
Combination
你可以将一个promise数组整合成一个整体的promise。成功时候的数组可以用
all
。如果你有一个数组的promise,你可以使用
spread
替代'then'。spread
方法会把成功处理程序返回的参数给“分开”。失败处理程序会访问第一个失败的信号。无论哪一个接收promise失败的函数都会首先由失败处理程序处理。不过
spread
最开始是all
的一种,所以你可以跳过它的链。all
方法返回一个数组的promise,当Promise的状态是成功的时候,在原始的promise中,这些数组会按照这些promise的顺序包含成功状态返回的值。如果其中一个给定的promise的状态为失败,那么返回的那个promise就会立即被拒绝,不必等待剩余的批次。如果你想要等到所有的promise确定其状态为成功或者失败,那么你可以使用allSettled
。any
方法接受一个promise数组,并返回一个promise的状态是成功(第一个给定的promise的状态为成功)或是失败(所有给定的promise的状态都为失败)。Sequences
如果你有一些promise-producing函数需要顺序执行,你可以像一下例子那样:
然而,如果你想运行一个动态构造的序列函数,你可以这样做:
通过使用
reduce
可以使此稍微更紧凑一点:你可以使用更紧凑的写法:
##### Handling Errors
Promise有一个有时不直观的方面的,如果你在成功状态时候抛出一个异常,此异常将不会被失败状态的处理程序捕获。
为什么会这样呢?考虑一下promise之间的平行关系和
try
/catch
。我们尝试去执行foo()
:错误处理程序类似于foo()
里的catch
,而成功处理程序表示在try
/catch
之后的代码块。此时,代码需要自身的try
/catch
代码块。对于promise而言,这意味着链接在你的失败处理程序里:
Progress Notification
让promise时刻反馈他们的进度是有可能的,例如:上传文件所需时间较长时。不是所有的promise对象都有进度反馈,但是对于那些需要知道其进度的,你可以传递第三个参数给
then
来对进度情况作操作:就像
fail
,Q提供了进度回调的缩写progress
:The End
当一条promise链快结束的时候,此时你要么返回最后一个promise,要么结束promise链。因为失败处理程序可以捕获异常,这是一个不好的模式,因为异常的去处不能被观察。
所以,要么返回它:
要么结束它:
结束一个promise链能够确保:如果一个错误没有在结束之前被失败处理程序捕获,它就会被重新载入并且报告。
我们正在探索如果去使得一个未处理的错误可见而无任何明确的处理。
The Beginning
上面的所有假设,你已经从某处得到一个promise,这是一种很常见的情况。每隔一段时间,你需要从头开始创建一个promise。
Using
Q.fcall
promise可以来自于一个使用
Q.fcall
的值,下面的例子返回一个IO promise:你也可以使用
fcall
创建一个带有异常的promise:顾名思义,
fcall
能称为一个函数,也能是一个promise方法。可以使用eventualAdd
方法去添加两个数字:Using Deferreds
如果你必须要用基于异步回调函数而不是基于promise,Q提供了一些简写方法(就像
Q.fcall
)。但是更多的时候,我们需要使用延迟去写解决方法。注意:延迟可以使用一个值或是一个promise对象来解决。
reject
方法是处理失败状态的promise的一个简写。这是一个简化
Q.delay
的写法:这是一个简化
Q.timeout
的写法:最后,你可以通过使用
deferred.notify
给promise发送一个进度情况报告。这是浏览器里的一个XML HTTP请求包装器。当然,只有在实践中才能更彻底地实施它。
下面是一个如何使用
requestOkText
方法的例子:Using
Q.promise
这是一个类似deferred概念的替代promise-creation API,但没有引入另一个概念实体。
下面用
Q.promise
方法写一个requestOkText
:如果
requestOkText
抛出了一个异常,返回的promise状态为失败,抛出的异常则为其异常原因。The Middle
如果你正在使用一个会返回一个promise的函数,可能只需要返回一个值并且不需要延迟,你可以使用Q库里面的'static'。
when
静态等效于then
:在promise里,所有的其他方法都有相对应的静态类似物。
下面的两段代码是等效的:
当使用其他库提供的promise时,你应该将其转换为Q promise。不是所有的库都保证其为Q,并且不一定会提供所有和Q一模一样的方法。很多库都只提供部分
then
方法。幸运的是,我们需要把它变成充满活力的Q promise。如果因为某些原因,你从你的库中获取到的promise不是一个Q promise,此时你应该使用Q函数包装它。你可以使用简写形式
Q.invoke
:Over the Wrie
Promise 可以作为另一个变量的代理,就算是一个远程的对象也是可以的。有很多方法可以帮助我们更容易地操作属性或者调用函数。所有的这些方法都返回promise对象,因此它们可以被链接在一起。
如果promise是一个远程对象的代理,你可以使用这些往返的方法,而不必使用
then
。采用远程对象promise的优势,可以查看Q-Connection 。就算是在非远程对象上,这些方法可以作为特别简单的成功状态处理程序的简写形式。例如,可以使用:
替代
Adapting Node
如果你正在使用Node.js的回调没事函数,并且回调函数的形式是
function(err,result)
,Q提供了一些有用的实用函数,用于它们之间进行转换。最直截了当的大概就是Q.nfcall
和Q.nfapply
(“Node里面的call/apply方法”)——Node风格的函数且返回一个promise对象。如果你正在使用方法而不是一个简单的功能实现,你能够很容易实际使用它在一个函数中传递一个方法到另一个函数——就像
Q.nfcall
——从方法的所有者中“解除绑定”。为了避免这种情况,你可以使用Function.prototype.bind
或是一个由我们提供的简写形式:你也可以通过
Q.denodeify
或Q.nbind
来包装一个可以重复使用的函数:最后,如果你正在使用一个原始延迟对象,可以使用
makeNodeResolver
方法来更有效地处理延迟问题:Long Stack Traces
Q提供了对“长堆栈跟踪”的可选支持,其中失败状态的原因所述的堆栈属性被重写为可以跟踪的异步跳转而不是第一个报错的地方。来看下面的例子:
通常会返回一个挺没用的长堆栈跟踪,就像:
但是如果你通过设置打开此功能:
上面的代码会返回一个很好的长堆栈跟踪反馈:
注意:你是如何看到函数在长堆栈跟踪中处罚异步操作的。这是非常有用的一个调bug的方法,否则你最终得到的只有第一行,
还有一堆在操作开始的地方Q内部没有任何符号的东西。
在 Node.js里,此功能能够通过
Q_DEBUG
环境变量来实现:这将在所有的Q实例中都支持长堆栈跟踪。
这个功能带来了严重的性能和内存开销,但是,如果你正在大量使用promise,或者是需要把服务器分配给很多用户,你就应该关闭这个设置。不过如果是在开发中,开启它吧。
Tests
你可以在浏览器中查看Q测试套件的结果。
License
Copyright 2009–2016 Kristopher Michael Kowal and contributors
MIT License (enclosed)
The text was updated successfully, but these errors were encountered: