如何在Javascript/jQuery中避免硬编码、链式异步函数?

如何在Javascript/jQuery中避免硬编码、链式异步函数?,javascript,jquery,ajax,Javascript,Jquery,Ajax,我的程序中几乎所有的函数都有某种异步调用,但它们都依赖于以前某个函数的结果。因此,我将下一个函数调用硬编码到每个单独的函数调用中: function getStuff() { $.ajax({ ... success: function(results) { // other functions involving results getMoreStuff(results); } })

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

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);
        }
    );
}
等等。这是一个大链,每个函数调用下一个函数。虽然这在程序中起作用,但会使每个函数单独失效

我不知道如何避免这个问题。我不知道如何使用常规回调函数,因为当我进行函数调用时,结果是这样的(使用上述函数):

但“结果”还没有定义

解决办法似乎很明显,我只是有点不明白。对不起

试试看

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

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


etc

传递接受参数的回调:

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中创建了一个完整的动画棋盘游戏)。

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

getStuff(function(results) {
    getMoreStuff(results, doSomethingWithStuff);
});
或者像这样,使用jQuery的
延迟
承诺
对象:

getStuff().then(getMoreStuff).then(doSomethingWithStuff):
使用回调 让
getStuff
getMoreStuff
都接受一个参数,该参数是完成后要调用的回调,例如:

function getStuff(callback) {
//                ^------------------------------ callback argument
    $.ajax({
        ...
        success: function(results) {
            // other functions involving results
            callback(results);
//          ^------------------------------------ use the callback arg
        }
    });
}
…对于
getMoreStuff
也是如此

使用
延期
承诺
jQuery的
ajax
功能与它的
Deferred
Promise
功能集成在一起。您只需将
return
添加到现有函数中即可,例如:

function getStuff(callback) {
    return $.ajax({
        ...
    });
}
(注意:不需要
success
回调。)

然后这个代码:

getStuff().then(getMoreStuff).then(doSomethingWithStuff);
这是否:

  • getStuff
    启动其
    ajax
    调用,并返回调用创建的
    Promise

  • ajax
    调用完成并解析承诺时,将调用
    getmoretuff
    ,并将
    ajax
    调用的结果作为其第一个参数。它启动它的
    ajax
    调用

  • getmoretuff
    ajax
    调用完成时,将使用该调用的结果调用
    doSomethingWithStuff
    (在
    getmoretuff
    中的一个)

  • 重要的是使用
    然后
    ,而不是
    完成
    ,以便在每个阶段传递正确的结果。(如果使用
    done
    ,则
    getMoreStuff
    doSomethingWithStuff
    都将看到
    getStuff
    ajax
    调用的结果。)

    下面是一个使用ajax的完整示例:

    |(更容易看到发生了什么)

    输出:

    getStuff starting ajax getMoreStuff got data from first request, starting ajax doSomethingWithStuff got data from second request one running one resolving Two: Got 'one' two resolving Three: Got 'two' three resolving …适用于可能有异步完成的任何情况

    下面是一个非ajax示例:

    输出:

    getStuff starting ajax getMoreStuff got data from first request, starting ajax doSomethingWithStuff got data from second request one running one resolving Two: Got 'one' two resolving Three: Got 'two' three resolving 请注意,我们使用它的方式没有改变:

    getStuff().then(getMoreStuff).then(doSomethingWithStuff);
    
    所有这些变化都发生在
    getStuff


    这是整个“promise”概念的一大优点(这一点对于jQuery来说并不是特别的,但是jQuery提供了方便的版本供我们使用),它对于解耦非常有用。

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

    $('body').trigger('joined-game', [game_id, response]);
    
    第一个参数是要发布的事件名称,第二个参数是数据数组

    最好的做法是在最特定的DOM元素上触发事件,但是如果您在多个页面上订阅了相同的事件,并且不确定DOM元素是否在所有页面上都存在,那么您可以在
    body
    上触发它,或者在所有页面上始终存在一些“转储/合成”不可见的DOM元素

    $("body").on('joined-game', function(event, game_id, response){
        //...
    });
    
    然后,您订阅要利用的事件。记住,除了数据之外,第一个参数始终是事件

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


    更多详细信息:

    将回调作为额外参数传递给函数。然后,调用函数可以决定在完成时执行什么操作。您的回调示例只需要返回一个参数。i、 e.
    getStuff(函数(结果){getMoreStuff(结果,函数(结果){doSomethingWithStuff(结果);});};
    getStuff(函数(结果)…
    现在结果被定义了。@Jay:我已经在我的答案中添加了一个完整的ajax示例,使用了
    Deferred
    Promise
    。这到底是如何将所需的结果参数传递给下一个调用的呢?@HiTechMagic在上述两种情况下,参数与ajax响应数据相同。据我所知,没有过程传递给链接函数调用的sed值…如果有任何已处理的结果,则可以接受经典回调参数,或者我们可以创建自己的不同对象。明白了…您链接了Ajax结果。可爱。我假设可能会进行更多的处理(示例中没有)。否则,为什么要麻烦首先传递它:)链不应该从getStuff()转到doSomethingWithStuff()吗一直以来?在我看来,您的示例只执行一个步骤链。最好是按照您的建议执行类似于下面的@HiTech的操作,但只执行延迟。@HiTechMagic是的,将进行更多的处理,然后将处理后的结果输入回调函数。非常抱歉,我在最初的问题中没有指定这一点。不过,我不提这个问题,因此当前的答案是有意义的。这会链接函数,但不会传递异步函数捕获的结果。@Zim84:当传递到
    的函数返回一个承诺时,下面的函数最终取决于该承诺,而给
    resolve
    的任何参数最终都会运行这适用于
    ajax
    ,也适用于您自己的函数。
    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);
    
    $('body').trigger('joined-game', [game_id, response]);
    
    $("body").on('joined-game', function(event, game_id, response){
        //...
    });