我正在尝试在Javascript中编写函数(如果需要,可以使用jQuery):
function fetchItem(itemId) { return /* ??? */; }
此函数依赖于第二个预定义且不可修改的函数,如下所示:
function load(callback) { /* ... */ }
此功能是异步的。调用它之后,它通过XHR获取n个项目,然后当它们到达时,将它们存储在DOM中,然后调用回调。
fetchItem
使用一个简单的jQuery选择器(此处不相关)来检查具有itemId
的元素的DOM,如果该项目还没有,则调用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 :(得分: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 :(得分:1)
我只是将你的fetchItem函数作为加载回调。像这样:
function fetchItem(itemId, callback):
if not dom.contains(itemId):
load(fetchItem)
else:
callback(dom.find(itemId))
callback()是一个函数,当必要元素出现在DOM中时,它会执行其余的工作。
答案 2 :(得分:0)
这是不可能的。您无法从异步创建同步性。为什么不为你的fetchItem
函数添加回调?
答案 3 :(得分: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);
});
感谢所有人鼓励我引入另一个回调,但事实并非如此糟糕。 :)