2010-11-25 56 views
1

我想使用JavaScript编写一个函数(使用jQuery,如果你想):如何将异步(基于回调)函数的执行封装到Javascript中的同步函数中?

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

此功能依赖于第二,预定义和unmodifyable功能,看起来像这样:

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

该功能是异步的。调用它后,它通过XHR获取n个项目,然后当它们到达时,将它们存储在DOM中,然后调用回调。

fetchItem使用简单的jQuery选择器(无关此处)检查元素的DOM与itemId,并且如果该项目尚不存在,则调用load。冲洗并重复。

我的问题是,我想的多load异步调用包装成我的同步fetchItem功能,这应该与itemId返回的DOM元素已经赚够了load来电之后。

伪代码,如果load是同步的:

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

我在Javascript中这样做,这可能显示了很多关于JavaScript的关闭和执行模型误解的第一次尝试:;)

function fetchItem(itemId) { 
    var match = undefined; 

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

    return match; 
} 

显然,这是因为在第一次回调之前执行return而失败。另外,正如您所看到的,我在match退回到fetchItem时遇到了一些问题。这里的封闭是否适当地保护了它?如果fetchItem并行执行多次,假设load支持这个(并且不会混淆DOM),这会工作吗?

我可能在这里失去了一个非常好的模式,但我真的不知道谷歌什么...

回答

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功能添加回调?

+0

回调本身创建异步例程的同步不是吗?一件事发生在另一件事之后。 – Orbling 2010-11-25 19:54:04

2

你需要让fetchItems异步过,并为其提供一个回调,这样的事情或许应该工作(未经测试的警告!):

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中时执行其余任务。