同步函数和异步延迟 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
的承诺运行的,并且竞争条件发生在 updateB
和 updateFinal
之间。
我可以用 .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()
处理程序的回调可以执行以下任何操作:
可以是同步的return一个常规值这个值成为promise链的resolved值然后后续.then()
处理程序被调用,将值传递给它们。
它可以是同步的而不是 return 一个值。 promise 链在那个时候有一个值 undefined
并且将调用链中的下一个 .then()
处理程序。
可以同步并抛出异常。异常成为当前promise和任何附加的.then()
拒绝处理程序的拒绝值,未调用解析处理程序。
它可以是异步的并且return一个promise。这个新的promise被插入到链中,随后的.then()
处理程序是在这个新的承诺完成之前不会被调用。是否调用后续的 resolve 或 reject 处理程序将取决于此新承诺是 resolve 还是 rejects。
它可以是异步的,而不是 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
我想发送 Ajax 请求并在数据到达时执行一些不相关的操作。完成操作后,我喜欢等待 Ajax 完成并执行另一个操作。
为了具体起见,让我们看一个简单的例子:
$.ajax({url: "/reqA"})
.done(updateA)
.done($.ajax("/reqB").done(updateB))
.done(updateFinal)
updateFinal
应在同步 updateA
和异步 /reqB
以及随后的同步 updateB
.
上面的代码是错误的,因为所有后续的 .done()
都是根据 /regA
的承诺运行的,并且竞争条件发生在 updateB
和 updateFinal
之间。
我可以用 .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()
处理程序的回调可以执行以下任何操作:
可以是同步的return一个常规值这个值成为promise链的resolved值然后后续
.then()
处理程序被调用,将值传递给它们。它可以是同步的而不是 return 一个值。 promise 链在那个时候有一个值
undefined
并且将调用链中的下一个.then()
处理程序。可以同步并抛出异常。异常成为当前promise和任何附加的
.then()
拒绝处理程序的拒绝值,未调用解析处理程序。它可以是异步的并且return一个promise。这个新的promise被插入到链中,随后的
.then()
处理程序是在这个新的承诺完成之前不会被调用。是否调用后续的 resolve 或 reject 处理程序将取决于此新承诺是 resolve 还是 rejects。它可以是异步的,而不是 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