PageRenderTime 61ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/_posts/2013-08-26-mastering-async-error-handling-with-promises.md

https://github.com/lidaling/nuysoft.github.com
Markdown | 469 lines | 334 code | 135 blank | 0 comment | 0 complexity | a83da6f318a4dec521748096a1df46d8 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause
  1. ---
  2. layout: post
  3. title: "用 Promises 控制异步错误处理"
  4. tagline: "Mastering Async Error Handling with Promises"
  5. description: ""
  6. category-substitution: 翻译
  7. tags: [翻译, JavaScript, Async, Promise, when]
  8. pgroup: "异步编程"
  9. ---
  10. {% include JB/setup %}
  11. <!-- As we saw in [Async Programming is Messy](http://know.cujojs.com/tutorials/async/async-programming-is-messy.html.md), error handling in callback-based asynchronous code gets messy quickly, and loses many of the qualities of synchronous code that make it familiar and easier to reason about. In [Simplifying Async with Promises](http://know.cujojs.com/tutorials/async/simplifying-async-with-promises.html.md), we introduced Promises and saw how they restore call-and-return semantics, allow errors to propagate up the stack similarly to synchronous exceptions, and generally provide a cleaner approach to managing asynchrony, especially when handling errors. -->
  12. 正如我们在 [凌乱的异步编程](/2013/08/26/async-programming-is-messy/) 一文中看到的基于回调函数的异步代码很快变得混乱起来并且大大降低了同步代码部分的质量从而更容易导致错误 [ Promises 简化异步编程](/2013/08/27/simplifying-async-with-promises/) 一文中我们引入了 Promises看到了它们如何恢复调用-返回编程模型并且提供一种更清晰方法来管理异步特别是在处理错误时
  13. <!-- ## Try/catch/finally -->
  14. ## Try/catch/finally
  15. <!-- In synchronous code, `try/catch/finally` provides a simple and familiar, yet very powerful idiom for performing a task, handling errors, and then always ensuring we can clean up afterward. -->
  16. 在同步代码中`try/catch/finally` 提供了一种简单友好但非常强大的惯用语法来执行任务处理错误并且总是确保稍后可以执行清理
  17. > 译注idiom [习语](http://baike.baidu.com/view/1678043.htm)
  18. <!-- Here's a simple `try/catch/finally` example in the same vein as the original `getTheResult()` from Part 1: -->
  19. 下面是一个简单的 `try/catch/finally` 示例 [Part 1](/2013/08/28/async-programming-is-messy/) 中的原始 `getTheResult()` 一模一样
  20. // Sync
  21. function getTheResult() {
  22. try {
  23. return thisMightFail();
  24. } catch(e) {
  25. return recoverFromFailure(e);
  26. } finally {
  27. alwaysCleanup();
  28. }
  29. }
  30. <!-- As we've seen, attempting to simulate even the `try/catch` via a callback-based approach is fraught with pitfalls. Adding the notion of `finally`, that is, *guaranteed cleanup*, only makes things worse. -->
  31. 正如我们已经看到的那样即使是试图以基于回调函数的方式来模拟 `try/catch` 也充满了陷阱加入 `finally` 的概念后*确保执行清理*只会使事情变得更糟
  32. <!-- Using Promises, we can build an approach that is analogous to this familiar `try/catch/finally` idiom, without deep callback structures. -->
  33. 使用 Promises我们可以建立一种方法类似于友好的惯用语法 `try/catch/finally`并且没有深度回调结构
  34. <!-- ## Try/catch -->
  35. ## Try/catch
  36. <!-- Let's start with a simpler version of example above that only uses `try/catch`, and see how we can use Promises to handle errors in the same way. -->
  37. 让我们从上面示例的简单版本开始只使用 `try/catch`然后看看如何用 Promises 以同样的方式处理错误
  38. // Sync
  39. function getTheResult() {
  40. try {
  41. return thisMightFail();
  42. } catch(e) {
  43. return recoverFromFailure(e);
  44. }
  45. }
  46. <!-- And now, as in Part 2, let's assume that `thisMightFail()` is asynchronous and returns a Promise. We can use `then()` to simulate `catch`: -->
  47. 现在就像在 [Part 2](/2013/08/27/simplifying-async-with-promises/) 中一样让我们假设 `thisMightFail()` 是异步的并且返回一个 Promise我们可以用 `then()` 来模拟 `catch`
  48. // Async
  49. function thisMightFail() {
  50. //...
  51. return promise;
  52. }
  53. function getTheResult() {
  54. return thisMightFail()
  55. .then(null, recoverFromFailure);
  56. }
  57. <!-- Waitaminit, that's *even less code* than using `try/catch`! What's going on here? -->
  58. 等等 `try/catch` *代码甚至更少*这是怎么回事呢
  59. > 译注Waitaminit, Wait a minute
  60. <!-- ### Propagating a success -->
  61. ### 传播成功状态
  62. <!-- This example introduces two very important facts about how Promises behave. The first of which is: -->
  63. 这个例子引入了两个关于 Promises 行为非常重要的事实其中第一个是
  64. <!-- If no `onFulfilled` handler is provided to `then()`, the fulfillment value will propagate through unchanged to the returned Promise. -->
  65. 如果没有提供 `onFulfilled` 处理程序结果值将原封不动地传播到返回的 Promise
  66. <!-- We're *not* supplying an `onFulfilled` handler when calling `then()`. This means that a successful result from `thisMightFail()` simply will propagate through and be returned to the caller. -->
  67. 在调用 `then()` 我们*没有*提供 `onFulfilled` 处理程序这意外着 `thisMightFail()` 返回的成功结果将简单地传播并返回给调用者
  68. <!-- ### Handling an error -->
  69. ### 处理错误
  70. <!-- The other important behavior is: -->
  71. 另外一个重要的行为是
  72. <!-- A handler may produce either a successful result by returning a value, or an error by throwing or returning a rejected promise. -->
  73. 处理程序可以通过返回一个值来产生一个成功结果也可以通过抛出错误或返回一个被拒的 Promise 来产生一个错误
  74. <!-- We *are* supplying an `onRejected` handler: `recoverFromFailure`. That means that any error produced by `thisMightFail` will be provided to `recoverFromFailure`. Just like the `catch` statement in the synchronous example, `recoverFromFailure` can handle the error and `return` a successful result, *or* it can produce an error by throwing or by returning a rejected Promise. -->
  75. 我们提供了一个 `onRejected` 处理程序`recoverFromFailure`这意外着`thisMightFail` 产生的任何错误将被传给 `recoverFromFailure`就像同步例子中的 `catch` 语句`recoverFromFailure` 可以处理传入的错误并 `return` 一个成功结果*也可以*通过抛出错误或返回一个被拒的 Promise 来产生一个错误
  76. <!-- Now we have a fully asynchronous construct that behaves like its synchronous analog, and is just as easy to write. -->
  77. 现在我们有一个完整的异步结构它的行为就像是同步的模拟并且也很容易编写
  78. <!-- ### Adding some sugar -->
  79. ### 添加一点语法糖
  80. > 译注Syntactic sugar [语法糖](http://baike.baidu.com/view/1805428.htm)
  81. <!-- Hmmm, but what about that `null` we're passing as the first param? Why should we have to type `null` everywhere we want to use this asynchronous `try/catch`-like construct? Can't we do better? -->
  82. 但我们把 `null` 作为第一个参数传入是什么意思在想要使用类似 `try/catch` 的异步结构的地方我们为什么必须要键入 `null`能不能做的更好点
  83. <!-- While the primary interface to a Promises/A+ Promise is its `then()` method, many implementations add convenience methods, built, with very little code, upon `then()`. For example, [when.js](https://github.com/cujojs/when) Promises provide an [`otherwise()` method](https://github.com/cujojs/when/blob/master/docs/api.md#otherwise) that allows us to write this example more intuitive and compactly: -->
  84. 虽然遵循 Promises/A+ 规范的 Promise 的主要接口是 `then()` 方法但是许多实现都用很少的代码 `then()` 为基础构建和添加了便捷方法例如[when.js](https://github.com/cujojs/when) Promises 提供了一个 [`otherwise()` 方法](https://github.com/cujojs/when/blob/master/docs/api.md#otherwise),允许我们更直观、更紧凑地编写这个例子:
  85. // Async: Using when.js promise.otherwise();
  86. function getTheResult() {
  87. return thisMightFail()
  88. .otherwise(recoverFromFailure);
  89. }
  90. <!-- Now we have something that reads nicely! -->
  91. 现在我们有了阅读起来很棒的异步结构
  92. <!-- ## Adding finally -->
  93. ## 添加 finally
  94. <!-- Let's add `finally` back into the mix, and see how we can use Promises to achieve the same result for asynchronous operations. -->
  95. 让我们把 `finally` 添加到这种混合结构中看看如何用 Promises 使异步操作达到同样的结果
  96. // Sync
  97. function getTheResult() {
  98. try {
  99. return thisMightFail();
  100. } catch(e) {
  101. return recoverFromFailure(e);
  102. } finally {
  103. alwaysCleanup();
  104. }
  105. }
  106. <!-- First, let's note that there are some very interesting things about this seemingly simple `finally` block. It: -->
  107. 首先让我们注意到的是这个看似简单的 `finally` 块包含了一些有趣的东西
  108. <!-- 1. will always execute after thisMightFail and/or recoverFromFailure
  109. 1. does not have access to the value returned by `thisMightFail`, or to the thrown exception (`e`), or to the value returned by `recoverFromFailure`[^1].
  110. 1. cannot, in this case, transform an exception thrown by `recoverFromFailure` back into a successful result[^2].
  111. 1. *can* change a successful result (returned by either `thisMightFail` or `recoverFromFailure`) into a failure if `alwaysCleanup` throws an exception.
  112. 1. *can* substitute a new exception in place of one thrown by `recoverFromFailure`. That is, if both `recoverFromFailure` and `alwaysCleanup` throw exceptions, the one thrown by `alwaysCleanup` will propagate to the caller, and the one thrown by `recoverFromFailure` *will not*. -->
  113. 1. 总是在 `thisMightFail` / `recoverFromFailure` 之后执行
  114. 2. <a name="footnote-1-ref"></a>没有机会访问 `thisMightFail` 返回的值或抛出的异常 `e`也没有机会访问 `recoverFromFailure` 返回的值[^1]
  115. 3. <a name="footnote-2-ref"></a>在这种情况下不能把 `recoverFromFailure` 抛出的异常转换回成功结果[^2]
  116. 4. 如果 `alwaysCleanup` 抛出一个异常*可以*把成功结果 `thisMightFail` `recoverFromFailure`转换为一个失败
  117. 5. *可以*用一个新异常替换掉 `recoverFromFailure` 抛出的异常也就是说如果 `recoverFromFailure` `alwaysCleanup` 都抛出了异常`alwaysCleanup` 抛出的异常将传播到调用者而由 `recoverFromFailure` 抛出的却*不会*
  118. <!-- This seems fairly sophisticated. Let's return to our asynchronous `getTheResult` and look at how we can achieve these same properties using Promises. -->
  119. 这似乎相当复杂让我们回到异步的 `getTheResult`看看如何用 Promises 实现同样的特性
  120. <!-- ### Always execute -->
  121. ### 总是会执行
  122. <!-- First, let's use `then()` to ensure that `alwaysCleanup` will execute in all cases (for succinctness, we'll keep when.js's `otherwise`): -->
  123. 首先让我们用 `then()` 确保 `alwaysCleanup` 在所有情况下都将会执行为了简洁些我们会保留 when.js `otherwise`
  124. // Async
  125. function getTheResult() {
  126. return thisMightFail()
  127. .otherwise(recoverFromFailure);
  128. .then(alwaysCleanup, alwaysCleanup);
  129. }
  130. <!-- That seems simple enough! Now, `alwaysCleanup` will be executed in all cases: -->
  131. 这似乎很简单现在`alwaysCleanup`在所有情况下都将会被执行
  132. <!-- 1. if `thisMightFail` succeeds,
  133. 1. if `thisMightFail` fails and `recoverFromFailure` succeeds, or
  134. 1. if `thisMightFail` and `recoverFromFailure` both fail. -->
  135. 1. 如果 `thisMightFail` 成功了
  136. 2. 如果 `thisMightFail` 失败了 `recoverFromFailure` 成功了
  137. 3. 如果 `thisMightFail` `recoverFromFailure` 都失败了
  138. <!-- But wait, while we've ensured that `alwaysCleanup` will always execute, we've violated two of the other properties: `alwaysCleanup` *will* receive the successful result or the error, so has access to either/both, and it *can* transform an error into a successful result by returning successfully. -->
  139. 但等等虽然我们已经确保了 `alwaysCleanup` 将总是会执行但是也侵犯了其他两项特性`alwaysCleanup` **收到成功结果或错误因此有机会访问其中之一或者两个并且*可以*通过返回一个成功值把一个错误转换为一个成功结果
  140. <!-- ### Don't access result/error -->
  141. ### 不要访问结果/错误
  142. <!--We can introduce a wrapper to prevent passing the result or error to `alwaysCleanup`:-->
  143. 我们可以引入一个包装函数以防把结果或错误传给 `alwaysCleanup`
  144. // Async
  145. function alwaysCleanupWrapper(resultOrError) {
  146. // don't pass resultOrError through
  147. return alwaysCleanup();
  148. }
  149. function getTheResult() {
  150. return thisMightFail()
  151. .otherwise(recoverFromFailure);
  152. .then(alwaysCleanupWrapper, alwaysCleanupWrapper);
  153. }
  154. <!-- Now we've achieved one of the two properties we had lost: `alwaysCleanup` no longer has access to the result or error. Unfortunately, we had to add some code that feels unnecessary. Let's keep exploring, though, to see if we can achieve the remaining property. -->
  155. 现在我们已经实现了曾丢掉的两项特性中的一项`alwaysCleanup` 不再可以访问结果或错误不幸的是我们不得不添加一些感觉没必要的代码不过让我们继续探索看看是否可以实现剩下的特性
  156. <!-- ### Don't change the result -->
  157. ### 不要改变结果
  158. <!-- While `alwaysCleanupWrapper` prevents `alwaysCleanup` from accessing the result or error, it still allows `alwaysCleanup` to turn an error condition into a successful result. For example, if `recoverFromFailure` produces an error, it will be passed to `alwaysCleanupWrapper`, which will then call `alwaysCleanup`. If `alwaysCleanup` returns successfully, the result will be propagated to the caller, thus squelching the previous error. -->
  159. 虽然 `alwaysCleanupWrapper` 阻止了 `alwaysCleanup` 访问结果或错误但是它仍然允许 `alwaysCleanup` 把一个错误状态转换一个成功结果例如如果 `recoverFromFailure` 产生一个错误它将被传给 `alwaysCleanupWrapper`然后调用 `alwaysCleanup`如果 `alwaysCleanup` 成功返回返回值将传播到调用者从而消除了之前的错误
  160. <!-- That doesn't align with how our synchronous `finally` clause behaves, so let's refactor: -->
  161. 这与同步的 `finally` 之句的行为并不匹配所以让我们重构它
  162. // Async
  163. function alwaysCleanupOnSuccess(result) {
  164. // don't pass result through, *and ignore* the return value
  165. // of alwaysCleanup. Instead, return original result to propagate it.
  166. alwaysCleanup();
  167. return result;
  168. }
  169. function alwaysCleanupOnFailure(error) {
  170. // don't pass result through, *and ignore* the result
  171. // of alwaysCleanup. Instead, rethrow error to propagate the failure.
  172. alwaysCleanup();
  173. throw error;
  174. }
  175. function getTheResult() {
  176. return thisMightFail()
  177. .otherwise(recoverFromFailure);
  178. .then(alwaysCleanupOnSuccess, alwaysCleanupOnFailure);
  179. }
  180. <!-- In both the success and failure cases, we've preserved the outcome: `alwaysCleanupOnSuccess` will execute `alwaysCleanup` but not allow it to change the ultimate result, and `alwaysCleanupOnFailure` will also execute `alwaysCleanup` and always rethrow the original error, thus propagating it even if `alwaysCleanup` returns successfully. -->
  181. 在成功和失败状态下我们已经保存了结果`alwaysCleanupOnSuccess` 将执行 `alwaysCleanup` 但不允许它改变最终结果`alwaysCleanupOnFailure` 也将执行 `alwaysCleanup` 并总是抛出原始的错误从而传播错误即使 `alwaysCleanup` 成功返回
  182. <!-- ### The remaining two properties -->
  183. ### 剩下的两项特性
  184. <!-- Looking at the refactor above, we can also see that the remaining two properties hold: -->
  185. 看看上面的重构我们还可以发现它涵盖了剩下的两项特性
  186. <!-- In `alwaysCleanupOnSuccess`, if `alwaysCleanup` throws, the `return result` will never be reached, and this new error will be propagated to the caller, thus turning a successful result into a failure. -->
  187. `alwaysCleanupOnSuccess` 如果 `alwaysCleanup` 抛出错误`return result` 将永远不会被执行到 并且这个新错误将传播到调用者从而把一个成功结果转换为一个失败结果
  188. <!-- In `alwaysCleanupOnFailure`, if `alwaysCleanup` throws, the `throw error` will never be reached, and the error thrown by `alwaysCleanup` will be propagated to the caller, thus substituting a new error. -->
  189. `alwaysCleanupOnFailure` 如果 `alwaysCleanup` 抛出错误`throw error` 将永远不会被执行到并且这个由 `alwaysCleanup` 抛出的错误将传播到调用者从而代之以一个新错误
  190. <!-- ## Finally? -->
  191. ## 圆满了吗
  192. <!-- With this latest refactor, we've created an asynchronous construct that behaves like its familiar, synchronous `try/catch/finally` analog. -->
  193. 通过最新重构的代码我们已经创建了这样一个异步结构它的行为就像友好的同步 `try/catch/finally` 的模拟
  194. <!-- ### More sugar -->
  195. ### 更多语法糖
  196. <!-- Some Promise implementations provide an abstraction for the `finally`-like behavior we want. For example, when.js Promises provide an [`ensure()` method](https://github.com/cujojs/when/blob/master/docs/api.md#ensure) that has all of the properties we achieved above, but also allows us to be more succinct: -->
  197. 一些 Promise 提供了类似 `finally` 行为的抽象例如when.js Promises 提供了一个 [`ensure()` 方法](https://github.com/cujojs/when/blob/master/docs/api.md#ensure),它具备我们前面实现的所有特性,但是更简洁:
  198. // Async: Using when.js promise.ensure();
  199. function getTheResult() {
  200. return thisMightFail()
  201. .otherwise(recoverFromFailure)
  202. .ensure(alwaysCleanup);
  203. }
  204. <!-- ## Finally -->
  205. ## 小结
  206. <!-- We started with the goal of finding a way to model the useful and familiar synchronous `try/catch/finally` behavior for asynchronous operations. Here's the simple, synchronous code we started with: -->
  207. 我们一开始的目标是为异步操作寻找一种方式来模拟有用且好用的同步 `try/catch/finally` 的行为下面是我们开始时的简单同步代码
  208. // Sync
  209. function getTheResult() {
  210. try {
  211. return thisMightFail();
  212. } catch(e) {
  213. return recoverFromFailure(e);
  214. } finally {
  215. alwaysCleanup();
  216. }
  217. }
  218. <!-- And here is the asynchronous analog we ended up with something that is just as compact, and easily readable: -->
  219. 然后下面是最终的异步模拟结构紧凑而且易于阅读
  220. // Async
  221. function getTheResult() {
  222. return thisMightFail()
  223. .otherwise(recoverFromFailure)
  224. .ensure(alwaysCleanup);
  225. }
  226. <!-- ## Try/finally -->
  227. ## Try/finally
  228. <!-- Another common construct is `try/finally`. It is useful in executing cleanup code, but always allowing exceptions to propagate in the case where there is no immediate recovery path. For example: -->
  229. 另一种常见的结构是 `try/finally`在执行清理代码时它很有用但是在这种没有恢复路径`catch`的情况下它总是允许异常传播例如
  230. // Sync
  231. function getTheResult() {
  232. try {
  233. return thisMightFail();
  234. } finally {
  235. alwaysCleanup();
  236. }
  237. }
  238. <!-- Now that we've modeled a full `try/catch/finally` using Promises, modeling `try/finally` is trivial. Similarly to simply cutting out the `catch` above, we can cut out the `otherwise()` in our Promise version: -->
  239. 现在我们已经 Promises 完整地模拟了 `try/catch/finally`模拟 `try/finally` 就小菜一碟了就像上面简单地删除掉 `catch` 一样我们可以在 Promise 版本中删去 `otherwise()`
  240. // Async
  241. function getTheResult() {
  242. return thisMightFail()
  243. .ensure(alwaysCleanup);
  244. }
  245. <!--All of the constraints we've been attempting to achieve still hold--this asynchronous construct will behave analogously to its synchronous `try/finally` counterpart.-->
  246. 我们一直试图实现的制约特性仍然保留了下来这个异步结构的行为类似于对应的同步 `try/finally`
  247. <!-- ## Using it -->
  248. ## 应用异步结构
  249. <!-- Let's compare how we would use the synchronous and asynchronous versions of `getTheResult`. Assume we have the following two pre-existing functions for showing results and errors. For simplicity, let's also assume that `showResult` might fail, but that `showError` will not fail. -->
  250. 让我们来比较一下如何使用 `getTheResult` 的同步和异步版本假设已经有下面两个用于展示结果和错误的函数为了简单起见我们还假设 `showResult` 可能会失败 `showError` 不会失败
  251. // Assume showResult might fail
  252. function showResult(result) { /* Format and show the result */ }
  253. // Assume showError will never fail
  254. function showError(error) { /* Show the error, warn the user, etc. */ }
  255. <!-- ### Synchronous -->
  256. ### 同步版本
  257. <!-- First, the synchronous version, which we might use like this: -->
  258. 首先是同步版本我们可能会像这样使用
  259. // Sync
  260. try {
  261. showResult(getTheResult());
  262. } catch(e) {
  263. showError(e);
  264. }
  265. <!-- It's quite simple, as we'd expect. If we get the result successfully, then we show it. If getting the result fails (by throwing an exception), we show the error. -->
  266. 正如我们预料的它相当简单如果我们成功地得到结果然后就展示结果如果得到失败的结果通过抛出一个异常就展示错误
  267. <!--It's also important to note that if `showResult` fails, we will show an error. This is an important hallmark of synchronous exceptions. We've written single `catch` clause that will handle errors from either `getTheResult` or `showResult`. The error propagation is *automatic*, and required no additional effort on our part.-->
  268. 同样需要重点注意的是如果 `showResult` 失败了将展示一个错误这是同步异常的一个重要标志我们写下的 `catch` 单句将处理来自 `getTheResult` `showResult` 的错误这种错误传播是*自动的*不需要为之增加额外的代码
  269. <!-- ### Asynchronous -->
  270. ### 异步版本
  271. <!-- Now, let's look at how we'd use the asynchronous version to accomplish the same goals: -->
  272. 现在让我们看看如何用异步版本完成同样的目标
  273. // Async
  274. getTheResult().then(showResult)
  275. .otherwise(showError);
  276. <!-- The functionality here is analogous, and one could argue that visually, this is even simpler than the synchronous version. We get the result, or rather in this case, a Promise for the result, and when the actual result materializes (remember, this is all asynchronous!), we show it. If getting the result fails (by rejecting resultPromise), we show the error. -->
  277. 这里的功能是类似的而且会惊讶于它看起来居然比同步版本更简单我们得到了结果或者更确切地说是结果的一个 Promise并且当真正的结果实现时记住一切都是异步的我们将展示它如果得到了失败的结果通过拒绝 Promise我们将展示错误
  278. <!-- Because Promises propagate errors similarly to exceptions, if `showResult` fails, we will also show an error. So, the automatic the behavior here is also parallel to the synchronous version: We've written single `otherwise` call that will handle errors from either `getTheResult` or `showResult`. -->
  279. 由于 Promises 像传播异常一样传播错误因此如果 `showResult` 失败了我们也会展示一个错误因此这种自动的行为与同步版本是也是等价的我们编写的 `otherwise` 单句调用将处理来自 `getTheResult` `showResult` 的错误
  280. <!-- Another important thing to notice is that we are able to use the same `showResult` and `showError` functions as in the synchronous version. We don't need artificial callback-specific function signatures to work with promises--just the same functions we'd write anyway. -->
  281. 另一件需要注意的事情是同样的 `showResult` `showError` 函数也可以使用在同步版本中我们不需要为了能在 Promises 中运行而人工改造特定函数的签名与我们在任意地方编写的函数完全一样
  282. <!-- ## Putting it all together -->
  283. ## 整合
  284. <!-- We've refactored our `getTheResult` code to use Promises to eumlate `try/catch/finally`, and also the calling code to use the returned Promise to handle all the same error cases we would handle in the synchronous version. Let's look at the complete Promise-based asynchronous version of our code: -->
  285. 我们已经重构了 `getTheResult` 的代码使之以 Promises 来模拟 `try/catch/finally`也使调用代码用返回的 Promise 来处理与同步版本相同的错误让我们完整地看看代码基于 Promise 的异步版本
  286. // Using getTheResult()
  287. getTheResult().then(showResult)
  288. .otherwise(showError);
  289. function getTheResult() {
  290. return thisMightFail()
  291. .otherwise(recoverFromFailure)
  292. .ensure(alwaysCleanup);
  293. }
  294. function thisMightFail() {
  295. // Using the proposed Promises/A+ style API for promise creation
  296. return makePromise(function(resolve, reject) {
  297. var result, error;
  298. // Do work, then:
  299. if(error) {
  300. reject(error);
  301. } else {
  302. resolve(result);
  303. }
  304. });
  305. }
  306. <!-- ## The end? -->
  307. ## 结语
  308. <!-- Of course, there will always be differences between synchronous and asynchronous execution, but by using Promises, we can narrow the divide. The synchronous and Promise-based versions we've constructed not only look very similar, they *behave* similarly. They have similar invariants. We can reason about them in similar ways. We can even *refactor* and *test* them in similar ways. -->
  309. 当然同步执行和异步执行之间总是有所差异但是我们可以通过使用 Promises 缩小这种差异同步版本和我们构建的基于 Promise 的版本不仅看起非常相似而且它们的*行为*也相似它们有着相似的固定格式我们可以用相似的方式揣测它们我们甚至能用相似的方式*重构**测试*它们
  310. <!-- Providing familiar and predictable error handling patterns and composable call-and-return semantics are two powerful aspects of Promises, but they are also only the beginning. Promises are a building block on which fully asynchronous analogs of many other familiar features can be built easily: higher order functions like map and reduce/fold, [parallel and sequential](https://github.com/cujojs/when/blob/master/docs/api.md#concurrency) task execution, and much more. -->
  311. 提供友好和可预测的错误处理模式以及可组合的调用-返回语法 Promises 两个强大的特性但是这仅仅是开始基于 Promises 的异步结构可以轻松地将许多其他功能完全异步化高级功能例如 mapreduce/fold[并行和顺序](https://github.com/cujojs/when/blob/master/docs/api.md#concurrency)的执行任务等等。
  312. <hr>
  313. <!--[^1]: You might be wondering why we want this property. For this article, we're choosing to try to model `finally` as closely as possible. The intention of synchronous `finally` is to cause *side effects*, such as closing a file or database connection, and not to transform the result or error by applying a function to it. Also, passing something that *might be a result or might be an error* to `alwaysCleanup` can be a source of hazards without *also* telling `alwaysCleanup` what kind of thing it is receiving. The fact that `finally` doesn't have a "parameter", like `catch` means that the burden is on the developer to grant access to the result or error, usually by storing it in a local variable before execution enters the `finally`. That approach will work for these promise-based approaches as well.-->
  314. <!--[^2]: Note that `finally` *is* allowed to squelch exceptions by *explicitly* returning a value. However, in this case, we are not returning anything explicitly. I've never seen a realistic and useful case for squelching an exception that way.-->
  315. [^1]: #footnote-1
  316. [^2]: #footnote-2
  317. 1. <a name="footnote-1"></a>你可能会奇怪于为什么我们需要这个特性在这篇文章中我们选择尝试尽可能近似地模拟 `finally`同步 `finally` 的意图是引发某种*副作用*例如关闭一个文件或数据库连接并不是执行一个函数来转换结果或错误而且 `alwaysCleanup` 传入一个*可能是结果或错误*的参数**不告诉 `alwaysCleanup` 正在接受的参数什么类型可能是一个危害源事实上`finally` 没有参数不像 `catch`这意外着开发人员需要承担授权访问结果或错误的烦扰通常的做法是在进入 `finally` 之前把结果或错误存储到一个局部变量中这种做法也可以应用在基于 Promise 的方式中<a href="#footnote-1-ref"></a>
  318. 2. <a name="footnote-2"></a>需要注意的是`finally`*可以*通过*明确地*返回一个值消除异常但是在这种情况下我们没有明确地返回任何东西我从来没有在现实中见过需要用这种方式来消除异常的情况<a href="#footnote-2-ref"></a>
  319. <hr>
  320. > 原文[Mastering Async Error Handling with Promises](http://know.cujojs.com/tutorials/async/mastering-async-error-handling-with-promises)
  321. <!-- https://github.com/know-cujojs/know/blob/master/src/documents/tutorials/async/mastering-async-error-handling-with-promises.html.md -->
  322. <link href="/assets/codemirror/lib/codemirror.css" rel="stylesheet">
  323. <link href="/assets/codemirror/theme/neat.css" rel="stylesheet">
  324. <script src="/assets/codemirror/lib/codemirror.js"></script>
  325. <script src="/assets/codemirror/addon/runmode/runmode.js"></script>
  326. <script src="/assets/codemirror/mode/javascript/javascript.js"></script>
  327. <script type="text/javascript">
  328. $('pre').each(function(index, el){
  329. $(this).hide()
  330. var ctn = $('<pre class="cm-s-neat">').insertAfter(this)
  331. CodeMirror.runMode($(this).find('code').text(), 'javascript',
  332. ctn.get(0));
  333. })
  334. </script>