Anti-promise Promise反模式
source link: https://segmentfault.com/a/1190000007395254
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Nested Promise(嵌套的Promise)
loadSomething().then(function(something) { loadAnotherthing().then(function(anthor) { DoSomethingOnThem(something, anthor); }) })
你这样书写的原因是需要对2个promise
的结果进行处理,由于then()
接收的是上一个promise返回的结果,因此你无法通过链式写法将其连接起来。
To Fix:
q.all([loadSomething(), loadAnotherThing()]) .spread(function(something, another) { DoSomethingOnThem(something, another); })
q.all()
方法将会等待loadSomething
和loadAnotherThing
都被resolve
后调用传入spread
方法里面的回调。
The Broken Chain(断掉的promise链)
先看一段代码:
function anAsyncCall() { var promise = doSomethingAsync(); promise.then(function() { somethingComplicated(); }) return promise; }
这种写法的问题是当somethingComplicated
方法中抛出错误无法被捕获。Promise
应当是链式的,每次调用then()
方法后都会返回一个新的promise
。普遍的写法是:最后调用catch()
方法,这样在前面的一系列的promise
操作当中,发生的任何error
都会被捕获。
上面的代码中,当你最后返回的是第一个promise
,而非这个promise
调用then()
方法后的结果,那么promise
链也随即断掉。
To Fix:
function anAsyncCall() { var promise = doSomethingAsync(); return promise.then(function() { somethingComplicated() }); }
The Collection Kerfuffle
当你有一个数组,数组里面的每个项目都需要进行异步的处理。因此你可能会通过递归去做某些事情:
function workMyCollection(arr) { var resultArr = []; function _recursive(idx) { if (idx >= resultArr.length) return resultArr; return doSomethingAsync(arr[idx]).then(function(res) { resultArr.push(res); return _recursive(idx + 1); }); } return _recursive(0); }
这段代码第一眼看上去有点难以理解啊。主要是的问题是如果不知道还有map
或者reduce
方法,那么你也不会知道有多少个项目需要被链接起来,这样就比较蛋疼了。
To Fix:
上面提到的q.all
方法接受一个数组,这个数组里面都是promise
,然后q.all
方法等待所有这些promise
全部被resolve
后,得到一个数组,由之前的promise
被resolve
后的值组成。这个时候我们可以通过map
方法去改进上面的代码:
function workMyCollection(arr) { return q.all(arr.map(function(item) { return doSomethingAsync(item); })); }
上面的递归写法是串行处理的,但是通过q.all
和map
进行改写后变成并行处理,因此更加高效。
如果你确实需要promise
串行处理,那么你可以使用reduce
方法:
function workMyCollection(arr) { return arr.reduce(function(promise, item) { return promise.then(function(result) { return doSomethingAsyncWithResult(item, result); }, q()); }); }
虽然不是很整洁,但是肯定有条理。
The Ghost Promise
有个方法有时需要异步进行处理,有时可能不需要。这时也可以创建一个promise
,去保证2种不同的处理方式的连贯性。
var promise ; if(asyncCallNeeded) { promise = doSomethingAsync(); } else { promise = Q.resolve(42); } promise.then(function() { doSomethingCool(); });
还有一种更加整洁的写法,就是使用Q()
方法去包裹一个普通值
或者promise
。
Q(asyncCallNeeded ? doSomethingAsync() : 42) .then(function(value) { dosomethingGood(); }) .catch(function(err) { handleTheError(); });
The Overly Keen Error Handler
then()
方法接收2个参数,fullfilled handler
以及rejected handler
:
somethingAync.then(function() { return somethingElseAsync(); }, function(err) { handleMyError(err); })
但是这种写法存在一个问题就是如果有错误在somethingElseAsync
方法中抛出,那么这个错误是无法被error handler
捕获的。
这个时候需要将error handler
单独注册到then()
方法中。
To Fix:
somethingAsync .then(function() { return somethingElseAsync() }) .then(null, function(err) { handleMyError(err); });
或者使用catch()
方法:
somethingAsync() .then(function() { return somethingElseAsync(); }) .catch(function(err) { handleMyError(err); });
这2种写法都为了保证在promise
链式处理过程中出现错误能被捕获。
The Forgotten Promise
你调用了一个方法并返回了一个promise
。然而,你忘记了这个promise
并且创建了一个你自己的:
var deferred = Q.defer(); doSomethingAsync().then(function(res) { res = manipulateMeInSomeWay(res); deferred.resolve(res); }, function(err) { deferred.reject(err); }); return deferred.promise(;
这里面存在着很多无用的代码,并且和Promise
简洁的思想正好相悖。
To Fix:
return doSomethingAsync().then(function(res) { return manipulateMeInSomeWay(res); });
相关资料:
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK