11

我的程序中几乎所有的函数都有某种异步调用,但它们都依赖于一些先前函数的结果。因此,我将下一个函数调用硬编码到每个单独的函数中:

function getStuff() {
    $.ajax({
        ...
        success: function(results) {
            // other functions involving results
            getMoreStuff(results);
        }
    });
}

function getMoreStuff(results) {
    $.ajax({
        ...
        success: function(moreResults) {
            // other functions involving moreResults
            doSomethingWithStuff(moreResults);
        }
    );
}

等等。这是一个大链,每个函数调用下一个函数。虽然这在程序中有效,但它使每个功能单独无用。

我对如何避免这个问题有点迷茫。我不知道如何使用一般的回调函数,因为当我进行函数调用时,结果是这样的(使用上面的函数):

getStuff(function() {
    getMoreStuff(results, doSomethingWithStuff);
};

但是“结果”还没有被定义。

解决方案似乎很明显,我只是对此有点密集。对不起!

4

4 回答 4

11

概述

你有几个选择。您可以使用回调使使用这些函数的代码如下所示:

getStuff(function(results) {
    getMoreStuff(results, doSomethingWithStuff);
});

或者像这样,使用 jQueryDeferredPromise对象:

getStuff().then(getMoreStuff).then(doSomethingWithStuff):

使用回调

两者都有getStuffgetMoreStuff接受一个参数,该参数是完成后要调用的回调,例如:

function getStuff(callback) {
//                ^------------------------------ callback argument
    $.ajax({
        ...
        success: function(results) {
            // other functions involving results
            callback(results);
//          ^------------------------------------ use the callback arg
        }
    });
}

...同样对于getMoreStuff.

使用DeferredPromise

jQuery 的ajax功能与其DeferredPromise特性相结合。您可以添加return到现有功能以使其正常工作,例如:

function getStuff(callback) {
    return $.ajax({
        ...
    });
}

(注意:不需要success回调。)

然后这段代码:

getStuff().then(getMoreStuff).then(doSomethingWithStuff);

做这个:

  1. getStuff开始其ajax调用并返回该Promise调用创建的。

  2. 当该ajax调用完成并解决 promise 时,getMoreStuff将调用结果ajax作为其第一个参数。 开始ajax调用。

  3. getMoreStuffajax调用完成时,使用该调用doSomethingWithStuff的结果(在 中的那个)调用getMoreStuff

重要的是使用then,而不是done,以便在每个阶段传递正确的结果。(如果你使用done,两者getMoreStuff doSomethingWithStuff都会看到getStuff'sajax调用的结果。)

这是使用的完整示例ajax

小提琴| 备用小提琴,ajax每个呼叫需要一秒钟(更容易看到正在发生的事情)

function getStuff() {
    display("getStuff starting ajax")
    return $.ajax({
        url: "/echo/json/",
        type: "POST",
        data: {json: '{"message": "data from first request"}'},
        dataType: "json"
    });
}

function getMoreStuff(results) {
    display("getMoreStuff got " + results.message + ", starting ajax");
    return $.ajax({
        url: "/echo/json/",
        type: "POST",
        data: {json: '{"message": "data from second request"}'},
        dataType: "json"
    });
}

function doSomethingWithStuff(results) {
    display("doSomethingWithStuff got " + results.message);
}

getStuff().then(getMoreStuff).then(doSomethingWithStuff);

function display(msg) {
    var p = document.createElement('p');
    p.innerHTML = String(msg);
    document.body.appendChild(p);
}

输出:

getStuff 启动 ajax

getMoreStuff 从第一个请求中获取数据,启动 ajax

doSomethingWithStuff 从第二个请求中获取数据

你不需要使用ajax来获得这个好处,你可以使用你自己的 DeferredPromise对象,这样你就可以编写这样的链:

one().then(two).then(three);

...对于您可能有异步完成的任何情况。

这是一个非ajax示例:

小提琴

function one() {
    var d = new $.Deferred();
    display("one running");
    setTimeout(function() {
      display("one resolving");
      d.resolve("one");
    }, 1000);
    return d.promise();
}

function two(arg) {
    var d = new $.Deferred();
    display("Two: Got '" + arg + "'");
    setTimeout(function() {
      display("two resolving");
      d.resolve("two");
    }, 500);
    return d.promise();
}

function three(arg) {
    var d = new $.Deferred();
    display("Three: Got '" + arg + "'");
    setTimeout(function() {
      display("three resolving");
      d.resolve("three");
    }, 500);
    return d.promise();
}

one().then(two).then(three);

function display(msg) {
    var p = document.createElement('p');
    p.innerHTML = String(msg);
    document.body.appendChild(p);
}

输出:

一个运行

一解决

二:得到“一个”

两个解决

三:得到“二”

三解决

这两个(ajax示例和非ajax示例)可以在必要时组合。例如,如果我们getStuffajax示例中获取并决定在将数据交给 之前必须对数据进行一些处理getMoreStuff,我们会像这样进行更改:Fiddle

function getStuff() {
    // Create our own Deferred
    var d = new $.Deferred();
    display("getStuff starting ajax")
    $.ajax({
        url: "/echo/json/",
        type: "POST",
        data: {json: '{"message": "data from first request"}', delay: 1},
        dataType: "json",
        success: function(data) {
            // Modify the data
            data.message = "MODIFIED " + data.message;

            // Resolve with the modified data
            d.resolve(data);
        }
    });
    return d;
}

请注意,我们使用它的方式没有改变:

getStuff().then(getMoreStuff).then(doSomethingWithStuff);

改变的一切都在getStuff.

这是整个“promise”概念的一大优点(这完全不是 jQuery 特有的,但 jQuery 为我们提供了方便的版本来使用),它非常适合解耦事物。

于 2013-08-09T10:16:02.477 回答
5

尝试

function getStuff() {
    return $.ajax({
        ...
        success: function(results) {
            // other functions involving results
        }
    });
}

function getMoreStuff(results) {
    return $.ajax({
        ...
        success: function(moreResults) {
            // other functions involving moreResults
        }
    );
}

然后

getStufff().done(function(){
    getMoreStuff().done(doSomethingWithStuff)
})

ETC

于 2013-08-09T10:12:49.970 回答
4

传递接受参数的回调:

function getStuff( callback ) {
    $.ajax({
        ...
        success: function(results) {
            // callback with result
            callback(results);
        }
    });
}

function getMoreStuff(results, callback) {
    $.ajax({
        ...
        success: function(moreResults) {
            // callback with result
            callback(moreResults);
        }
    );
}

function doSomethingWithStuff(results, callback) {
    // process results via some means not described herein :)
    if (callback){
        // callback yet again with results, but only if callback provided this time
        callback(stillMoreResults);
    }
}

然后使用类似的东西:

getStuff(function(results) { 
    getMoreStuff(results, function(moreresults){
             doSomethingWithStuff(moreresults);
        });
    };

这种模式通常对任何异步操作都很有用。它并不特定于 Ajax 调用(我用它在 JQuery 中创建了一个完整的动画棋盘游戏)。

于 2013-08-09T10:14:06.293 回答
2

解决方案非常简单。你必须使用Publish–subscribe模式。使用 jQuery 最简单的实现:

$('body').trigger('joined-game', [game_id, response]);

第一个参数是您要发布的事件名称,第二个参数是数据数组。

最佳实践是在最具体的 DOM 元素上触发事件,但是如果您在多个页面上订阅相同的事件,并且不确定 DOM 元素是否存在于所有页面上,您可以触发它body或一些“转储/合成”不可见 DOM 元素始终出现在所有页面上。

$("body").on('joined-game', function(event, game_id, response){
    //...
});

然后你订阅你想要使用的事件。请记住,除了您的数据之外,第一个参数始终是事件。

此解决方案的另一个优点是您可以将代码拆分为多个文件。

更多细节:http ://webility.pl/en/blog/entry/chaining-javascript-functions-without-dependecy-hell

于 2013-08-09T11:36:28.840 回答