2013-02-11 74 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