使用promise Q库实现异步JavaScript编程的最佳实践

时间:2013-08-30 15:16:30

标签: javascript asynchronous asp.net-web-api deferred q

JS中已有一个图层,它通过以下实现帮助获取和发布到服务器:

var getJson = function(url, callback, onError) {
    $.get(url)
    .done(function(data) {
        if(callback != null)
            callback(data);
    })
    .fail (function(error) {
        if(onError != null)
            onError (error);
        else
            my.notification.notifyError(onErrorMessage);
    });
};

var postJSON = function(url, data, callback, onError) {
    $.ajax({
        url : url ,
        type: "POST" ,
        contentType : "application/json"
        dataType : "json" ,
        date : ko.toJSON(data)
    })
    .done(function(data) {
        if(callback ! = null)
            callback(data);
    })
    .fail(function(error) {
        if(onError ! = null)
            onError (error);
        else
            my.notification.notifyError(onErrorMessage);
    });
};

在DataService层上使用这些实现:

// Get
var find = function(date, onSuccess , onError) {
   var url = /* url with the Controller and Action */ + "?queryString = " + data.filter;
   getJson(url , onSuccess , onError);
};

// Post
var save = function(date, onSuccess , onError) {
    var url = /* url with the Controller and Action */;
    postJSON(url, data, onSuccess, onError);
};

然而,我们使用webapi,在某些情况下,请求取决于另一个请求生成“金字塔末日”的结果。 为了更优雅的代码,我们正在实现用于异步编程的库Q.

使用Q promisses按照上面显示的模式实现了get as show的新方法:

var getJsonDefer = function(url, callback, onError) {
    return Q.when($.getJSON(url))
    .then (function(data) {
        if(callback ! = null)
            callback(data);
    })
    .fail (function(error) {
        if(onError ! = null)
            onError (error);
        else
            my.notification.notifyError(onErrorMessage);
    });
};

我正试图以这种方式在DataService层上使用此实现:

// Get
var find = function(date, onSuccess , onError) {
   var url = /* url with the Controller and Action */ + "?queryString = " + data.filter;
   return getJsonDefer(url, onSuccess, onError);
};

无论如何在我的图层视图模型javascript中假设我需要使用3个查找,而一个取决于另一个的结果:

var = dataOne { 
    filter: " Filter"
};

findOne(dataOne,
       function(result) {
            return result;
       }
       function(error) {
           throw error;
       })
       .then(function(args) {
            var = datatwo { 
                filter: args
            };

            // Second
            findTwo(datatwo ,
               function(result) {
                    return result;
               }
               function(error) {
                   throw error;
               }
            );
        })
        .then(function(args) {
            var = dataThree { 
                filter: args
            };

            // Third
            findThree(dataThree,
                function(result) {
                    return result;
                }
                function(error) {
                    throw error;
                }
            );
        }).catch(function(error) {
            // Handle any error from all above steps
        })
        .done();

我的问题:

我承认我无法以正确的方式实现,因为.then()中的所有函数都带有未定义的args。

我想知道在这里提出的方案是什么样的最佳做法。

1 个答案:

答案 0 :(得分:2)

我认为你会发现承诺的吸引力在于你可以用更少的代码来实现你的目标。但是,您需要了解一些事项。首先,您不再需要传递或接收回调和错误。您只需确保在处理程序中返回结果或承诺。这就是值传播到下一个处理程序的方式。

这是对您的程序的未经测试的修改,应该说明形式:

var find = function(data) {
    var url = /* url with the Controller and Action */ + "?queryString = " + data.filter;
    return Q($.getJson(url));
};

find({filter: "filter"})
.then(function (firstResult) {
    return find({filter: firstResult})
    .then(function (secondResult) {
        return find({filter: secondResult})
        .then(function (thirdResult) {
            return [firstResult, secondResult, thirdResult];
        });
    });
})
.fail(notifyError)
.done();

请注意,任何阶段的错误都将由底部的fail次调用处理。无论您最后是否有错误处理程序,始终使用done()结束链,以便之前发生的任何错误(即使在fail处理程序中)都会显示在您的控制台中。

请注意,如果一个操作依赖于前一个操作,并且处理程序需要访问第一个和第二个结果,则只需要嵌套promise。如果您只需要第二次操作的结果,则可以链接。

find({filter: "filter"})
.then(function (firstResult) {
    return find({filter: firstResult})
})
.then(function (secondResult) {
    return find({filter: secondResult})
    .then(function (thirdResult) {
        return [secondResult, thirdResult];
    });
});
.fail(notifyError)
.done();

您还可以使用Q.allpromise.spread来展平事物,但此时我会留给documentation,因为我希望您能获得要点。