2010-11-25 6 views
1

(あなたがしたい場合は、jQueryを使って)私はJavaScriptで関数を記述しようとしています:非同期(コールバックベース)関数の実行をJavascriptの同期関数にラップするにはどうすればよいですか?

function fetchItem(itemId) { return /* ??? */; } 

この関数は次のようになります第二、事前に定義されたとunm​​odifyable機能に依存しています:

function load(callback) { /* ... */ } 

この関数は非同期です。それを呼び出した後、XHRを介してn個の項目をフェッチし、到着したらDOMに格納してからコールバックを呼び出します。

fetchItemitemIdと要素のDOMを確認するために、単純なjQueryのセレクタ(無関係ここ)を使用し、アイテムがまだ存在しない場合loadを呼び出します。すすぎ、繰り返します。

私の問題は、私はそれが十分load通話をした後itemIdでDOM要素を返す必要があり、私の同期fetchItem機能へloadの複数の非同期呼び出しをラップしたいということです。

擬似コード、loadが同期した場合:おそらく、Javascriptのの閉鎖や実行モデルについての誤解の多くを表示するJavaScriptでこれを行うことで

function fetchItem(itemId): 
    while not dom.contains(itemId): 
     load() 
    return dom.find(itemId) 

私の最初の試み、:;)

function fetchItem(itemId) { 
    var match = undefined; 

    function finder() { 
     match = $(...).get(0); 
     if(!match) { 
      load(finder); 
     } 
    } 
    finder(); 

    return match; 
} 

returnが最初のコールバックの前に実行されるため、明らかにこれは失敗します。また、あなたが見ることができるように、私はmatchfetchItemに戻すいくつかの問題を抱えていました。ここは閉鎖によって適切に保護されていますか? loadがこれをサポートしていると仮定して、fetchItemが複数回並列に実行された場合(これはDOMを混在させません)

私はおそらくここに完全に良好なパターンを欠けているが、私は本当にするためにGoogleのか分からない...

答えて

0

誰もが私は自分のコールバックを導入する必要があることに同意するようなので、ここで私の(これまでに最終)ワーキングソリューションです、ようだ:別のコールバックを導入するために私を促進するためのみんなに

var MAX_FETCH_MORE = 3; 

/* 
* Searches for itemId, loading more items up to MAX_FETCH_MORE times if necessary. When 
* the item has been found or the maximum reload count has been reached, the callback 
* is invoked, which is passed the DOM object of the item wrapped in a jQuery object, or 
* undefined. 
*/ 
function executeWithItem(itemId, callback, fetchCycleCounter) { 
    // initialize fetchCycleCounter on first iteration 
    if(!fetchCycleCounter) fetchCycleCounter = 0; 
    console.debug('iteration ' + fetchCycleCounter + '/' + MAX_FETCH_MORE); 

    // try to find the item in the DOM 
    match = $('div[data-item-id="' + itemId + '"]').get(0); 
    if(match) { 
     // if it has been found, invoke the callback, then terminate 
     console.debug('found: ' + match); 
     callback($(match)); 
    } else if(!match && fetchCycleCounter < MAX_FETCH_MORE) { 
     // if it has not been found, but we may still reload, call load() and pass it 
     // this function as the callback 
     console.debug('fetching more...'); 
     load(function() {executeWithItem(itemId, callback, fetchCycleCounter+1);}); 
    } else { 
     // give up after MAX_FETCH_MORE attempts, maybe the item is gone 
     console.debug('giving up search'); 
    } 
} 

// example invocation 
executeWithItem('itemA01', function(item) { 
    // do stuff with it 
    item.fadeOut(10000); 
}); 

おかげで、それはしていませんとても悪く見えました。 :)

0

不可能であること。非同期性から同期性を作成することはできません。 fetchItem関数にもコールバックを追加しないでください。あなたもfetchItems非同期を作成し、それをコールバックを提供する必要が

+0

コールバック自体が非同期ルーチンからの同期性を作成しますか?一つ後に起こっていること。 – Orbling

+0

@Orbling:いいえ、そうではありません。最初に2つの非同期呼び出しのうちどれが終了したかを予測することはできません(同期は同期になります)。 – jwueller

+0

いいえ。しかし、コードの一部が非同期呼び出しに続くように、または複数の非同期呼び出しが行われた後でも、コードを設定できます。疑問の唯一の点は、複数の非同期呼び出しが開始されたときの戻り順です。 – Orbling

2

は、このようなものは、おそらく(!未テスト警告)動作するはずです:

function fetchItems(itemIDS, callback, matches) { 
    if (!matches) { // init the result list 
     matches = []; 
    } 

    // fetch until we got'em all 
    if (itemIDS.length > 0) { 
     var id = itemIDS[0]; // get the first id in the queue 
     var match = $(id).get(0); 

     // not found, call load again 
     if (!match) { 
      load(function() { 
       fetchItems(itemIDS, callback, matches); 
      }); 

     // found, update results and call fetchItems again to get the next one 
     } else { 
      matches.push(match); // push the current match to the results 
      itemIDS.shift(); // remove the current id form the queue 
      fetchItems(itemIDS, callback, matches); 
     } 

    // we have all items, call the callback and supply the matches 
    } else { 
     callback(matches); 
    } 
} 

fetchItems(['#foo', '#bar', '#test'], function(matches) { 
    console.log(matches); 
}) 
1

は、私は単純にロードするためにコールバックとしてごfetchItem機能を与えただろう。このように:

function fetchItem(itemId, callback): 
    if not dom.contains(itemId): 
     load(fetchItem) 
    else: 
     callback(dom.find(itemId)) 

callback()は、必要な要素がDOMに表示されたときに残りの作業を行う関数です。

関連する問題