1

Anti-promise Promise反模式

 3 years ago
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.
neoserver,ios ssh client

文章来源:Promise Anti-patterns

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()方法将会等待loadSomethingloadAnotherThing都被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后,得到一个数组,由之前的promiseresolve后的值组成。这个时候我们可以通过map方法去改进上面的代码:

    function workMyCollection(arr) {
        return q.all(arr.map(function(item) {
            return doSomethingAsync(item);
        }));
    }

上面的递归写法是串行处理的,但是通过q.allmap进行改写后变成并行处理,因此更加高效。

如果你确实需要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);
    });

相关资料:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK