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

Mix of synchronous functions and asynchronous deferred jQuery objects

我想发送 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 解决方案?

在启动异步并稍后加入异步调用结果后,是否有通往 运行 同步代码的其他路径?

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

在承诺链中,您可以将同步函数或异步函数作为 .then() 处理程序传递。如果你传递一个异步函数,它只需要 return 一个 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)

当您像以前一样直接在括号内执行函数时,它会被立即调用,执行该函数的 return 结果就是作为 .then() 处理程序传递的结果。当您传递一个函数引用时,.then() 基础架构可以稍后调用该函数。


您使用 $.when() 也可以。当您想并行执行多个项目并想知道它们何时全部完成时使用它。上面的代码推荐做序列化执行(一个接着一个)


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

  1. 可以是同步的return一个常规值这个值成为promise链的resolved值然后后续.then() 处理程序被调用,将值传递给它们。

  2. 它可以是同步的而不是 return 一个值。 promise 链在那个时候有一个值 undefined 并且将调用链中的下一个 .then() 处理程序。

  3. 可以同步并抛出异常。异常成为当前promise和任何附加的.then()拒绝处理程序的拒绝值,未调用解析处理程序。

  4. 它可以是异步的并且return一个promise。这个新的promise被插入到链中,随后的.then()处理程序是在这个新的承诺完成之前不会被调用。是否调用后续的 resolve 或 reject 处理程序将取决于此新承诺是 resolve 还是 rejects。

  5. 它可以是异步的,而不是 return 任何东西或 return 常规 (non-promise) 值。 在在这种情况下,启动了一个新的异步操作,但它对当前的承诺链没有影响,因为没有与新的异步操作相关的承诺被 returned。这个新的异步操作只是按照自己的节奏自行运行,当它完成时对当前的承诺链没有影响。由于 return 没有从 .then() 处理程序中编辑任何承诺,因此当前的承诺就好像这是一个同步处理程序一样进行,如 describe

我很难弄清楚你到底需要什么,但我认为它是这样的:

// 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);

我相信上面也可以写成下面的形式——不知道updateA函数的具体细节我只是猜测

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

我用过 .then 而不是 .done - 我不太熟悉 jQuery 的独特性和 non-compliant(相对于 Promise/A+ spec) 对 Promises 的解释——你可能想要,甚至需要在上面代码的某些地方使用 .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 then chains 和 Promise.all(类似于 $.when)中你很少需要这样的 kludge