是否有一个JavaScript预处理器使回调看起来不错?

时间:2013-02-11 08:47:06

标签: javascript asynchronous preprocessor

像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。如果你喜欢这个想法,请偷走它。我现在还买不起另一个项目。如果你找到一些工作代码,那么在评论中链接到你的存储库会很棒。

3 个答案:

答案 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?

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