混合同步函数和异步延迟jQuery对象

时间:2016-01-07 01:54:15

标签: javascript jquery asynchronous promise jquery-deferred

我希望在数据到达时发送Ajax请求并执行一些不相关的操作。完成操作后,我喜欢等待Ajax完成并执行其他操作。

具体来看看剥离的例子:

$.ajax({url: "/reqA"})
    .done(updateA)
    .done($.ajax("/reqB").done(updateB))
    .done(updateFinal)
完成同步updateFinal和异步updateA以及同步/reqB后,应执行

updateB

以上代码是错误的,因为所有后续.done()操作来自/regA的承诺,竞争条件发生在updateBupdateFinal之间。

我可以使用.then修复代码:

$.ajax({url: "/reqA"})
    .done(updateA)
    .then($.ajax("/reqB").done(updateB))
    .done(updateFinal)

但接下来我想在发送请求updateA之后运行/reqB(因为JS引擎单线程和updateA执行阻止了异步进程/reqB !!)。

以下代码无效:

$.ajax({url: "/reqA"})
    .then($.ajax("/reqB").done(updateB))
    .done(updateA)
    .done(updateFinal)

因为updateA执行将延迟到updateB执行!!

我认为问题可以通过$.when函数解决,但updateA不承诺,我在官方$.when文档中看不到执行保证的顺序。它可能看起来像:

$.ajax({url: "/reqA"})
    .then(
         $.when(
              $.ajax("/reqB").done(updateB),
              fakeResolvedPromiseWrapperAroundSyncFunc(updateA)
         )
    ).done(updateFinal)

jQuery库中是否有任何标准fakeResolvedPromiseWrapperAroundSyncFunc解决方案?

在启动异步并稍后加入异步调用结果后运行同步代码的任何其他路径?

2 个答案:

答案 0 :(得分:4)

.done().then()处理程序中,您需要传递函数REFERENCES,而不仅仅是调用parens中的函数,以便能够控制执行的时间。

在promise链中,您可以将同步函数或异步函数作为.then()处理程序传递。如果你传递一个异步函数,它只需要返回一个promise,那个promise就会被插入到promise链中。同步函数将在轮到它时被调用,然后链中的下一步将紧随其后(因为它是同步的)。

例如,更改此内容:

$.ajax({url: "/reqA"})
    .done(updateA)
    .done($.ajax("/reqB").done(updateB))
    .done(updateFinal)

到此:

$.ajax({url: "/reqA"})
    .then(updateA)
    .then(function() {
         return $.ajax("/reqB").then(updateB)
     }).then(updateFinal);

或者,这可以写成:

$.ajax({url: "/reqA"})
    .then(updateA)
    .then(function() {
         return $.ajax("/reqB");
     }).then(updateB)
     .then(updateFinal);

这将执行ajax函数,当Ajax调用完成时,它将执行updateA()。同步updateA()完成后,它将调用匿名函数,然后执行/ reqB ajax调用。当该ajax调用完成时,它将执行updateB()。完成updateB()后,updateFinal()将被调用。

并且,改变这个:

$.ajax({url: "/reqA"})
    .then($.ajax("/reqB").done(updateB))
    .done(updateA)
    .done(updateFinal)

到此:

$.ajax({url: "/reqA"})
    .then(function() {
        return $.ajax("/reqB").then(updateB);
     }).then(updateA)
    .then(updateFinal)

当您直接在parens中执行函数时,它被称为IMMEDIATELY,执行函数的返回结果是作为.then()处理程序传递的内容。当您传递函数引用时,.then()基础结构可以稍后调用该函数。

您对$.when()的使用也可行。当您想要并行执行多个项目并想知道它们何时完成时,可以使用它。上面的代码建议执行序列化执行(一个接一个)。

对于混合同步和异步代码,可以完美地工作。序列中的第一个操作必须创建一个可以链接.then()处理程序的promise。之后,任何给定.then()处理程序的回调可以执行以下任何操作:

  1. 它可以是同步的并返回常规值。此值成为promise链的已解析值,然后调用后续的.then()处理程序将该值传递给它们。

  2. 它可以是同步的,不会返回值。此时,promise链的值为undefined,链中的下一个.then()处理程序将被援引。

  3. 它可以是同步的并抛出异常。异常成为当前promise的被拒绝值,并且任何附加的.then()拒绝处理程序,而不是调用处理程序被调用。< / p>

  4. 它可以是异步的并返回一个promise。这个新的promise会被插入到链中,并且在满足这个新的promise之前不会调用后续的.then()个处理程序。是否调用后续解析或拒绝处理程序将取决于此新承诺是解决还是拒绝。

  5. 它可以是异步的,不返回任何内容或返回常规(非承诺)值。在这种情况下,会启动一个新的异步操作,但它对当前的promise链,因为没有返回与新的异步操作相关的promise。这个新的异步操作只是按照自己的节奏运行,当它完成时对当前的promise链没有影响。由于.then()处理程序没有返回任何承诺,因此当前的承诺就好像这是描述的同步处理程序

答案 1 :(得分:1)

我有点难以确定你需要什么,但我认为它是这样的:

// start ajax A
var ajax_A = $.ajax({url: "/reqA"});

// wait for ajax A to complete before starting ajax B
var ajax_B = ajax_A.then(function() {
    return $.ajax({url: "/reqB"});
});

// perform the updates when the respective ajax completes
var done_A = ajax_A.then(updateA);
var done_B = ajax_B.then(updateB);

// once ajax/update done for both A and B, run updateFinal
$.when(done_A, done_B).then(updateFinal);

我相信上面的内容也可以写成如下 - 不知道函数更新的具体细节我只是猜测

$.ajax({url: "/reqA"})
.then(function(resultA) {
    return $.when(updateA(resultA), $.ajax({url: "/reqB"}).then(updateB))
})
.then(updateFinal);

我使用.then而不是.done - 我真的不熟悉jQuery对Promise的独特和不合规(关于Promise / A +规范)解释 - 你可能想要,或者甚至需要在上面代码的某些地方使用.done

我刚刚阅读了jquery延迟文档,在上面的代码中似乎应该使用.done来代替.then - 但是我不能确定,我想你可能知道更好,因为你似乎熟悉jQuery deferred

至于 fakeResolvedPromiseWrapperAroundSyncFunc - 使用本机承诺(如果可用),只需

Promise.resolve(syncFunction());

是的,()应该在那里,因为你想解决调用同步功能的结果

不确定jQuery是否具有等价物......但这可能有效

var resolveIt(value) {
    var deferred = $.deferred;
    deferred.resolve(value);
    return deferred.promise();
}

resolveIt(syncFunction());

请注意,至少在Promise / A + spec中,然后链和Promise.all(类似于$ .when)你很少需要这样的kludge