繁体   English   中英

如何将异步(基于回调)函数的执行包装到Javascript中的同步函数中?

[英]How do I wrap executions of asynchronous (callback-based) functions into a synchronous function in Javascript?

我试图用Javascript(如果需要,可以用jQuery)编写一个函数:

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

此函数依赖于第二个预定义且不可修改的函数,如下所示:

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

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

fetchItem使用一个简单的jQuery选择器(此处不相关)来检查具有itemId的元素的DOM,并在项目尚不存在时调用load 冲洗并重复。

我的问题是我想将多个异步load调用包装到我的同步fetchItem函数中,该函数在进行了足够的load调用后应返回带有itemId的DOM元素。

伪代码,如果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),那么这项工作是否可行?

我可能在这里错过了一个非常好的模式,但是我真的不知道该怎么用Google搜索...

您还需要使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);
})

我只是给您的fetchItem函数作为要加载的回调。 像这样:

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

callback()是一个函数,当必要元素出现在DOM中时,它将完成其余工作。

那是不可能的。 您不能从异步创建同步。 为什么不也将回调添加到fetchItem呢?

似乎每个人都同意我需要引入自己的回调,所以这是我(到目前为止)的工作解决方案:

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

感谢大家鼓励我引入另一个回调,事实证明它看起来还不错。 :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM