像jQuery这样的JavaScript工具包都是关于回调函数的,而这些回调通常是匿名定义的。示例:某些网页显示表格中的消息列表。要更新此表,它可能首先要求服务器提供所有当前消息的列表(作为ID),然后检索尚未知的消息ID的内容:
function fnUpdateMessages() {
$.ajax({
type: 'POST',
data: { action: 'get_message_ids' },
success: function(sData) {
var aMessageIds = sData.split(/,/);
var aUnknownIds = fnWhichIdsAreNotInTable(aMessageIds);
$.ajax({
type: 'POST',
data: {
action: 'get_message_contents',
ids: aUnknownIds.join(',')
},
success: function(oData) {
for (var id in oData.messages) {
fnInsertMessage(oData.messages[id]);
}
}
);
}
);
}
你知道我要去哪里吗?这段代码很难看,因为只有2次后续的AJAX调用后缩进才是6级。我当然可以将匿名函数拆分为文件范围内的单独函数,但这通常会污染命名空间(除非通过将其包装在另一个匿名函数调用中进一步混乱)并且它打破了这些函数之间的强大联系:回调应该真的不是自己用的;它们就像原始fnUpdateMessages
函数的第二和第三部分一样。
我更想要的是这样的事情:
function fnUpdateMessages() {
$.ajax({
type: 'POST',
data: { action: 'get_message_ids' },
success: continue(sData)
});
var aMessageIds = sData.split(/,/);
var aUnknownIds = fnWhichIdsAreNotInTable(aMessageIds);
$.ajax({
type: 'POST',
data: {
action: 'get_message_contents',
ids: aUnknownIds.join(',')
},
success: continue(oData)
);
for (var id in oData.messages) {
fnInsertMessage(oData.messages[id]);
}
}
这个片段引入了新的假设语法continue(var1, var2, [...])
,它定义了一个匿名回调函数,其正文是封闭函数作用域中的所有内容。这使得这些回调函数看起来像同步代码。显然,这是必须预处理的,因为它不是标准的JS。
在我考虑编写这样的预处理器之前,我想知道这样的事情是否已经存在?
P.S。如果你喜欢这个想法,请偷走它。我现在还买不起另一个项目。如果你找到一些工作代码,那么在评论中链接到你的存储库会很棒。
答案 0 :(得分:1)
您可以使用jQuery的延迟来链接回调,而不是将它们包含在选项中。
function fnUpdateMessages() {
$.ajax({
type: 'POST',
data: { action: 'get_message_ids' }
).done(function(sData) {
var aMessageIds = sData.split(/,/);
var aUnknownIds = fnWhichIdsAreNotInTable(aMessageIds);
$.ajax({
type: 'POST',
data: {
action: 'get_message_contents',
ids: aUnknownIds.join(',')
}
}).done(function(oData) {
for (var id in oData.messages) {
fnInsertMessage(oData.messages[id]);
}
});
});
}
它并不完美,但每次请求它会为您节省几个级别的缩进。
有关详细信息,请参阅$.ajax的文档。
答案 1 :(得分:1)
只有两种解决方案:
第一个非常糟糕:您必须使第一个ajax请求同步,但您的脚本将阻塞,直到结果可用。 这真的是一个糟糕的解决方案,你不应该让任何ajax请求同步。
第二个使用jQuery.pipe函数对延迟对象返回$ .ajax(你必须使用jquery> 1.5)。 您可以使用像这样的管道链接回调(我使用内部函数使其更具可读性):
[编辑]:因为jquery 1.8,你应该使用deferred.then而不是deferred.pipe:
function fnUpdateMessages() {
var getMessages = function() {
return $.ajax({
type: 'POST',
data: { action: 'get_message_ids' },
});
};
var getContents = function(aUnknownIds) {
return $.ajax({
type: 'POST',
data: {
action: 'get_message_contents',
ids: aUnknownIds.join(',')
},
});
};
var insertMessages = function(oData) {
for (var id in oData.messages) {
fnInsertMessage(oData.messages[id]);
}
};
getMessages()
.then(getContents)
.done(insertMessages);
}
答案 2 :(得分:1)
是的。它被称为jwacs - JavaScript With Advanced Continuation Support。简而言之,您可以使用continuation来暂停程序的执行。然后,您可以通过调用continuation来恢复程序的执行。延续始终保留程序创建时的状态。
这有点像trampolining in JavaScript,但蹦床取决于仅受Mozilla产品支持的生成器 - Firefox和Rhino。如果你对trampolining感兴趣,我写了一个库,使写入异步可以忍受。它被称为Fiber,它有点像合作的Java线程。
另一方面,jwacs编译成普通的JavaScript。因此,它可以在任何平台上使用。不只是Firefox和Rhino。如果您想了解哪些延续,那么我建议您阅读以下StackOverflow问题和答案:
问题:What's the difference between a continuation and a callback?