3

像 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。

在我考虑编写这样的预处理器之前,我想知道这样的东西是否已经存在?

PS如果你喜欢这个想法,请偷走它。我现在买不起另一个项目。如果您获得一些工作代码,在评论中指向您的存储库的链接会很棒。

4

3 回答 3

2

只有两种解决方案:

第一个非常糟糕:您必须使第一个 ajax 请求同步,但您的脚本将阻塞,直到结果可用。这确实是一个糟糕的解决方案,您不应该使任何 ajax 请求同步。

第二个在 $.ajax 的延迟对象返回上使用 jQuery.pipe 函数(你必须使用 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);
     }
于 2013-02-11T09:30:07.720 回答
1

您可以使用 jQuery 的 deferreds 来链接回调,而不是将它们包含在选项中。

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的文档。

于 2013-02-11T09:20:48.697 回答
1

就在这里。它被称为jwacs - 具有高级延续支持的 JavaScript。简单地说,您使用延续来暂停程序的执行。然后,您可以通过调用延续来恢复程序的执行。延续始终保留程序在创建时的状态。

这有点像JavaScript 中的蹦床,但蹦床依赖于仅由 Mozilla 产品(Firefox 和 Rhino)支持的生成器。如果您对蹦床感兴趣,我编写了一个库来使异步编写变得可以忍受。它被称为Fiber,它有点像协作 Java 线程。

另一方面,jwacs 编译成普通的 JavaScript。因此它可以在任何平台上使用。不仅仅是火狐和犀牛。如果您想了解什么是延续,那么我建议您阅读以下 StackOverflow 问题和答案:

问题:延续和回调有什么区别?

答案:https ://stackoverflow.com/a/14022348/783743

于 2013-02-11T09:36:39.887 回答