我要花很多时间解决一个问题,我想知道是否有现成的解决方案。它是基于浏览器的应用程序(JavaScript 和 Dojo 工具包),是 RESTful Web 服务的客户端。
它使用 Comet 自动更新显示。有一个回调函数可以处理收到的每条消息。 [Comet 的无聊细节:作为一种后台操作,向服务器发出 HTTP 请求。这个请求在服务器上阻塞,直到它有一条消息给我们。当客户端最终收到响应时,它调用回调函数,然后发出下一个 HTTP 请求。 HTTP 最多允许两个同时请求,因此这个“后台”请求不会阻止用户执行操作时发生的“前台”请求。]
在UI层下面有一个适配器层。 UI 层认为它正在将消息推送给它。适配器层执行 Comet 请求并将响应从服务器发送的内容转换为 UI 层期望的内容。
var ourEventFilter = dojo.hitch(this, function(evt) {
if (evt["obj"]) {
evt.obj = this.transform(evt.obj);
}
callUIEventHandler(evt);
}
[dojo.hitch() 是一个语法糖,用于创建闭包,绑定(bind)函数的 this
。]
obj
可能看起来像这样:
{
"resources": [
{"name":"Me", "type":"vm", "link":"http://server/item/ABC"},
{"name":"You", "type":"real", "link":"http://server/item/123"}],
"subObjs": [
"resources":[{"name":"Him", "type":"vm", "link":"http://server/item/DEF"}
]
}
transform 函数把它变成这样:
{
"resources": [
{"name":"You", "type":"real", "link":"http://server/item/123"},
],
"vms": [
{"name":"Me", "type":"vm", "link":"http://server/item/ABC"}],
"subObjs:" [
"resources":[],
"vms": [{"name":"Him","type":"vm", "link":"http://server/item/DEF"}]
]
}
我们找到那些类型为“vm”的“资源”,并将它们移动到一个单独的数组中。到目前为止,一切都很好。 transform 函数很简单。它是递归的,因为 subOjbs 可以包含 subObjs。
但是现在我们需要更多关于 vms 的信息。我们需要对服务器进行 Ajax 调用以获取此信息:
{
"resources": [
{"name":"You", "type":"real", "link":"http://server/item/123"}],
"vms": [
{"name":"Me", "type":"vm", "link":"http://server/item/ABC", "moreInfo":"X"}],
"subObjs:" [
"resources":[],
"vms": [{"name":"Him","type":"vm", "link":"http://server/item/DEF",
"moreInfo":"Y"}]
]
}
转换函数看起来像这样:
transform: function(obj) {
var vms=[];
var newResources = [];
// Recurse on subObjs
if (obj.subObjs) {
for (var kx = 0; kx < obj.subObjs.length; kx++) {
ojb.subObjs[kx] = this.transform(obj.subObjs[kx]);
}
// Move vms out of resources into vms
if (obj.resources) {
for (var jx = 0; jx < obj.resources.length; jx++) {
if (obj.resources[jx].type == "vm") {
var thisVM = obj.resources[jx];
// Note: more info needed here.
//thisVM = this.getMoreInfo(thisVM);
vms.push(thisVM);
} else {
newResources.push(obj.resources[jx];
}
}
obj.vms = vms;
obj.resources = newResources;
}
return obj;
}
现在我们遇到了一个问题。 getMoreInfo() 怎么写?
此时我可以进行同步调用:
getMoreInfo: function(vm) {
vmObj = callServerSynchronouslyToGET(vm.link);
vm.moreInfo = vmObj ? vmObj.moreInfo : null;
}
但是同步调用在 Ajax 中从来都不是一个好主意(它应该是 Sjax)。
我认为不可能这样编写 getMoreInfo() 来进行异步调用。我必须返回到洋葱的几层并从某个点向下重写所有内容,我希望不要重写 Comet-callback 层之上的任何内容。
我知道一个将递归函数转换为非递归函数的方法。有没有一种方法可以将一个以同步 GET 为中心的循环的洋葱转换成异步 GET 链?
最佳答案
有一个名为 StratifiedJS 的 JavaScript 语言的跨浏览器扩展。
它旨在精确解决您提到的问题:它允许您以同步方式编程,所有内容都在幕后异步执行。
在浏览器上启用 StratifiedJS 的 JS 库称为“Oni Apollo”。参见 http://onilabs.com/apollo了解更多详情。
在您的特定情况下,您可以使用 StratifiedJS 将整个异步洋葱转换为同步代码,或者您可以通过将现有逻辑粘贴到“text/sjs”脚本元素中来保留现有逻辑:
<script src="http://code.onilabs.com/latest/oni-apollo.js"></script>
<script type="text/sjs">
// your existing code here
getMoreInfo: function(vm) {
var vmObj = require('http').get(vm.link);
vm.moreInfo = vmObj ? vmObj.moreInfo : null;
}
</script>
此处,require('http').get() 在后台执行异步 XHR(有关详细信息,请参阅上述链接中的 apollo api 文档)。
关于javascript - 寻找食谱 : Multiple async Ajax requests in JavaScript,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3702070/