diff --git a/docs/promise.md b/docs/promise.md index 0594c21..fceadad 100644 --- a/docs/promise.md +++ b/docs/promise.md @@ -509,12 +509,12 @@ Promise.all([ `Promise.race`方法同样是将多个Promise实例,包装成一个新的Promise实例。 ```javascript -var p = Promise.race([p1,p2,p3]); +var p = Promise.race([p1, p2, p3]); ``` -上面代码中,只要`p1`、`p2`、`p3`之中有一个实例率先改变状态,`p`的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给`p`的回调函数。 +上面代码中,只要`p1`、`p2`、`p3`之中有一个实例率先改变状态,`p`的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给`p`的回调函数。 -`Promise.race`方法的参数与`Promise.all`方法一样,如果不是Promise实例,就会先调用下面讲到的`Promise.resolve`方法,将参数转为Promise实例,再进一步处理。 +`Promise.race`方法的参数与`Promise.all`方法一样,如果不是 Promise 实例,就会先调用下面讲到的`Promise.resolve`方法,将参数转为 Promise 实例,再进一步处理。 下面是一个例子,如果指定时间内没有获得结果,就将Promise的状态变为`reject`,否则变为`resolve`。 @@ -768,3 +768,115 @@ run(g); 上面代码的Generator函数`g`之中,有一个异步操作`getFoo`,它返回的就是一个`Promise`对象。函数`run`用来处理这个`Promise`对象,并调用下一个`next`方法。 +## Promise.try() + +实际开发中,经常遇到一种情况:不知道函数`f`是同步函数,还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管`f`是否包含异步操作,都用`then`方法指定下一步流程,用`catch`方法处理`f`抛出的错误。一般就会采用下面的写法。 + +```javascript +Promise.resolve().then(f) +``` + +上面的写法有一个缺点,就是如果`f`是同步函数,那么它会在下一轮事件循环执行。 + +```javascript +const f = () => console.log('now'); +Promise.resolve().then(f); +console.log('next'); +// next +// now +``` + +上面代码中,函数`f`是同步的,但是用 Promise 包装了以后,就变成异步执行了。 + +那么有没有一种方法,让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API 呢?回答是可以的,并且还有两种写法。第一种写法是用`async`函数来写。 + +```javascript +const f = () => console.log('now'); +(async () => f())(); +console.log('next'); +// now +// next +``` + +上面代码中,第一行是一个立即执行的匿名函数,会立即执行里面的`async`函数,因此如果`f`是同步的,就会得到同步的结果;如果`f`是异步的,就可以用`then`指定下一步,就像下面的写法。 + +```javascript +(async () => f())() +.then(...) +``` + +需要注意的是,`async () => f()`会吃掉`f()`抛出的错误。所以,如果想捕获错误,要使用`promise.catch`方法。 + +```javascript +(async () => f())() +.then(...) +.catch(...) +``` + +第二种写法是使用`new Promise()`。 + +```javascript +const f = () => console.log('now'); +( + () => new Promise( + resolve => resolve(f()) + ) +)(); +console.log('next'); +// now +// next +``` + +上面代码也是使用立即执行的匿名函数,执行`new Promise()`。这种情况下,同步函数也是同步执行的。 + +鉴于这是一个很常见的需求,所以现在有一个[提案](https://github.com/ljharb/proposal-promise-try),提供`Promise.try`方法替代上面的写法。 + +```javascript +const f = () => console.log('now'); +Promise.try(f); +console.log('next'); +// now +// next +``` + +事实上,`Promise.try`存在已久,Promise 库[`Bluebird`](http://bluebirdjs.com/docs/api/promise.try.html)、[`Q`](https://github.com/kriskowal/q/wiki/API-Reference#promisefcallargs)和[`when`](https://github.com/cujojs/when/blob/master/docs/api.md#whentry),早就提供了这个方法。 + +由于`Promise.try`为所有操作提供了统一的处理机制,所以如果想用`then`方法管理流程,最好都用`Promise.try`包装一下。这样有[许多好处](http://cryto.net/~joepie91/blog/2016/05/11/what-is-promise-try-and-why-does-it-matter/),其中一点就是可以更好地管理异常。 + +```javascript +function getUsername(userId) { + return database.users.get({id: userId}) + .then(function(user) { + return uesr.name; + }); +} +``` + +上面代码中,`database.users.get()`返回一个 Promise 对象,如果抛出异步错误,可以用`catch`方法捕获,就像下面这样写。 + +```javascript +database.users.get({id: userId}) +.then(...) +.catch(...) +``` + +但是`database.users.get()`可能还会抛出同步错误(比如数据库连接错误,具体要看实现方法),这时你就不得不用`try...catch`去捕获。 + +```javascript +try { + database.users.get({id: userId}) + .then(...) + .catch(...) +} catch (e) { + // ... +} +``` + +上面这样的写法就很笨拙了,这时就可以统一用`promise.catch()`捕获所有同步和异步的错误。 + +```javascript +Promise.try(database.users.get({id: userId})) + .then(...) + .catch(...) +``` + diff --git a/docs/reference.md b/docs/reference.md index c8ad598..9fe1e82 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -127,6 +127,18 @@ - Axel Rauschmayer, [Pitfall: not all objects can be wrapped transparently by proxies](http://www.2ality.com/2016/11/proxying-builtins.html) - Bertalan Miklos, [Writing a JavaScript Framework - Data Binding with ES6 Proxies](https://blog.risingstack.com/writing-a-javascript-framework-data-binding-es6-proxy/): 使用 Proxy 实现观察者模式 +## Promise对象 + +- Jake Archibald, [JavaScript Promises: There and back again](http://www.html5rocks.com/en/tutorials/es6/promises/) +- Tilde, [rsvp.js](https://github.com/tildeio/rsvp.js) +- Sandeep Panda, [An Overview of JavaScript Promises](http://www.sitepoint.com/overview-javascript-promises/): ES6 Promise入门介绍 +- Dave Atchley, [ES6 Promises](http://www.datchley.name/es6-promises/): Promise的语法介绍 +- Axel Rauschmayer, [ECMAScript 6 promises (2/2): the API](http://www.2ality.com/2014/10/es6-promises-api.html): 对ES6 Promise规格和用法的详细介绍 +- Jack Franklin, [Embracing Promises in JavaScript](http://javascriptplayground.com/blog/2015/02/promises/): catch 方法的例子 +- Ronald Chen, [How to escape Promise Hell](https://medium.com/@pyrolistical/how-to-get-out-of-promise-hell-8c20e0ab0513#.2an1he6vf): 如何使用`Promise.all`方法的一些很好的例子 +- Jordan Harband, [proposal-promise-try](https://github.com/ljharb/proposal-promise-try): Promise.try() 方法的提案 +- Sven Slootweg, [What is Promise.try, and why does it matter?](http://cryto.net/~joepie91/blog/2016/05/11/what-is-promise-try-and-why-does-it-matter/): Promise.try() 方法的优点 + ## Iterator - Mozilla Developer Network, [Iterators and generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators) @@ -154,16 +166,6 @@ - Axel Rauschmayer, [ES6 generators in depth](http://www.2ality.com/2015/03/es6-generators.html): Generator规格的详尽讲解 - Derick Bailey, [Using ES6 Generators To Short-Circuit Hierarchical Data Iteration](https://derickbailey.com/2015/10/05/using-es6-generators-to-short-circuit-hierarchical-data-iteration/):使用 for...of 循环完成预定的操作步骤 -## Promise对象 - -- Jake Archibald, [JavaScript Promises: There and back again](http://www.html5rocks.com/en/tutorials/es6/promises/) -- Tilde, [rsvp.js](https://github.com/tildeio/rsvp.js) -- Sandeep Panda, [An Overview of JavaScript Promises](http://www.sitepoint.com/overview-javascript-promises/): ES6 Promise入门介绍 -- Dave Atchley, [ES6 Promises](http://www.datchley.name/es6-promises/): Promise的语法介绍 -- Axel Rauschmayer, [ECMAScript 6 promises (2/2): the API](http://www.2ality.com/2014/10/es6-promises-api.html): 对ES6 Promise规格和用法的详细介绍 -- Jack Franklin, [Embracing Promises in JavaScript](http://javascriptplayground.com/blog/2015/02/promises/): catch方法的例子 -- Ronald Chen, [How to escape Promise Hell](https://medium.com/@pyrolistical/how-to-get-out-of-promise-hell-8c20e0ab0513#.2an1he6vf): 如何使用`Promise.all`方法的一些很好的例子 - ## 异步操作和Async函数 - Luke Hoban, [Async Functions for ECMAScript](https://github.com/lukehoban/ecmascript-asyncawait): Async函数的设计思想,与Promise、Gernerator函数的关系