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

Jay*_*Jay 11 javascript ajax jquery

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

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);
        }
    );
}
Run Code Online (Sandbox Code Playgroud)

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

我对如何避免这个问题有点失落.我无法弄清楚如何使用通用回调函数,因为当我进行函数调用时,它会像这样结束(使用上面的函数):

getStuff(function() {
    getMoreStuff(results, doSomethingWithStuff);
};
Run Code Online (Sandbox Code Playgroud)

但是,"结果"还没有定义.

解决方案似乎很明显,我只是对它有点密集.抱歉!

T.J*_*der 11

概观

你有几个选择.您可以使用回调来使用这些函数看起来像这样的代码:

getStuff(function(results) {
    getMoreStuff(results, doSomethingWithStuff);
});
Run Code Online (Sandbox Code Playgroud)

或者像这样,使用jQuery DeferredPromise对象:

getStuff().then(getMoreStuff).then(doSomethingWithStuff):
Run Code Online (Sandbox Code Playgroud)

使用回调

同时拥有getStuffgetMoreStuff接受一个参数,这个参数在完成后调用,例如:

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

......同样如此getMoreStuff.

使用DeferredPromise

jQuery的ajax功能,其集成DeferredPromise功能.您只需添加return到现有功能即可实现,例如:

function getStuff(callback) {
    return $.ajax({
        ...
    });
}
Run Code Online (Sandbox Code Playgroud)

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

然后这段代码:

getStuff().then(getMoreStuff).then(doSomethingWithStuff);
Run Code Online (Sandbox Code Playgroud)

做这个:

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

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

  3. getMoreStuffajax调用完成,doSomethingWithStuff被称为具有的结果调用(一中getMoreStuff).

使用then而不是done为了在每个阶段获得正确的结果是很重要的.(如果你使用done,都getMoreStuff doSomethingWithStuff会看到结果getStuffajax电话.)

这是一个完整的例子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);
}
Run Code Online (Sandbox Code Playgroud)

输出:

getStuff starting ajax

getMoreStuff got data from first request, starting ajax

doSomethingWithStuff got data from second request

您不需要使用它ajax来获得这方面的好处,您可以使用自己的 DeferredPromise对象,这可以让您编写如下链:

one().then(two).then(three);
Run Code Online (Sandbox Code Playgroud)

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

这是一个非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);
}
Run Code Online (Sandbox Code Playgroud)

输出:

one running

one resolving

Two: Got 'one'

two resolving

Three: Got 'two'

three resolving

必要时可以组合这两个(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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,我们如何使用它并没有改变:

getStuff().then(getMoreStuff).then(doSomethingWithStuff);
Run Code Online (Sandbox Code Playgroud)

所有改变都在内getStuff.

这是关于整个"承诺"概念的一个伟大的事情(它完全不是jQuery特有的,但jQuery为我们提供了方便的版本),它非常适合解耦.


Aru*_*hny 5

尝试

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

function getMoreStuff(results) {
    return $.ajax({
        ...
        success: function(moreResults) {
            // other functions involving moreResults
        }
    );
}
Run Code Online (Sandbox Code Playgroud)

然后

getStufff().done(function(){
    getMoreStuff().done(doSomethingWithStuff)
})
Run Code Online (Sandbox Code Playgroud)

等等