2013-02-11 101 views
3

像jQuery这样的JavaScript工具包都是关于回调函数的,这些回调函数经常被匿名定义。示例:某个网页显示表格中的消息列表。要更新此表,它可能会首先向服务器请求所有当前的消息(如ID)的列表,然后检索内容为尚未未知消息ID:是否有JavaScript预处理器,使回调看起来不错?

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]); 
       } 
      } 
     ); 
     } 
    ); 
} 

你看我要去哪里?这段代码很难看,因为缩进只有在后续的两次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

https://nicolas.perriault.net/code/2013/flatten-javascript-pyramids-with-async-js /也许这篇文章会帮助你? – 2013-02-11 08:49:50

+0

不污染你可以使用闭包的命名空间,使回调看起来像一个对象中的私有函数,并使用该对象的一个​​实例来完成工作......或者你可以使用coffeescript或typescript ... – 2013-02-11 08:57:06

+0

@limelights:看起来有趣的,但乍一看,这并不能帮助我,因为我的行为本质上不是可并行化的。 – 2013-02-11 09:20:05

回答

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); 
    } 
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

是的。它叫做jwacs - JavaScript With Advanced Continuation Support。简单地说,你可以利用延续来暂停程序的执行。然后你可以通过调用continuation继续执行程序。继续始终保留程序在创建时的状态。

这有点像trampolining in JavaScript,但蹦床取决于只支持Mozilla产品的生成器 - Firefox和Rhino。如果你对蹦床感兴趣,我写了一个库来使异步可写。它被称为Fiber,它有点像合作Java线程。

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

问题:What's the difference between a continuation and a callback?

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