【问题标题】:How do I execute a function after the callbacks inside a for loop are completed?for 循环内的回调完成后如何执行函数?
【发布时间】:2012-07-01 19:46:43
【问题描述】:

我在搜索函数中有一个 for 循环,其中一个执行回调的函数在循环内执行回调,我想在循环之后执行 BUILD() 函数,并且在所有回调完成之后。我不知道该怎么做,因为循环在所有回调完成之前完成。回调是获取我数据的 API 请求,我想用该数据 BUILD()。

我阅读了 deferred,所以我尝试将 for 循环放在 deferred 的函数中,然后在 '.then( ... )' 上调用 BUILD()。但这似乎不起作用 - 我想我理解错了。

帮助?!

注意,这是使用 Google Maps Places API(搜索和获取详细信息)。

var types = {
    'gym' : 'fitness, gym',
    'grocery_or_supermarket': ''
}

function search() {
    for (var key in types) {
         var request = { ... };
         service.search(request, searchCallback);
    }
    // PROBLEM AREA
    BUILD();
}

function searchCallback(results, status) {
    for (var i = 0; i < results.length; i++) {
        var request = { ... };
        service.getDetails(request, detailsCallback);
    }
}

function detailsCallback(place, status) {
    // add place marker to maps and assign info window and info window event
}

【问题讨论】:

    标签: javascript google-maps-api-3 callback


    【解决方案1】:

    对你的代码稍加修改,就可以实现。

    var total = 1337; // Some number
    var internal_counter = 0;
    var fn_callback = function() {
        searchCallback.apply(this, arguments);
        if (++internal_counter === total) {
            BUILD();
        }
    };
    for (var i=0; i<total; i++) {
        service.search(request, fn_callback);
        ...
    

    说明

    首先,我们创建一个本地函数和变量。

    • 该变量是一个计数器,在调用回调时增加。
    • 该函数被传递给异步方法(service.search),该方法调用原始回调。增加计数器后,根据包含迭代总数的变量检查计数器的值。如果它们相等,则调用整理函数 (BUILD)。

    一个复杂的案例:处理嵌套回调。

    var types = { '...' : ' ... ' };
    
    function search() {
        var keys = Object.keys(types);
        var total = keys.length;
        // This counter keeps track of the number of completely finished callbacks
        //  (search_callback has run AND all of its details_callbacks has run)
        var internal_counter = 0;
    
        for (var i=0; i<total; i++) {
            var request = { '...' : ' ... ' };
            services.search(request, fn_searchCallback);
        }
    
        // LOCAL Function declaration (which references `internal_counter`)
        function fn_searchCallback(results, status) {
            // Create a local counter for the callbacks
            // I'm showing another way of using a counter: The opposite way
            // Instead of counting the # of finished callbacks, count the number
            //  of *pending* processes. When this counter reaches zero, we're done.
            var local_counter = results.length;
            for (var i=0; i<results.length; i++) {
                service.getDetails(request, fn_detailsCallback);
            }
            // Another LOCAL function (which references `local_counter`)
            function fn_detailsCallback(result, status) {
    
                // Run the function logic of detailsCallback (from the question)
                // " ... add place marker to maps and assign info window ... "
    
                // Reduce the counter of pending detailsCallback calls.
                //   If it's zero, all detailsCallbacks has run.
                if (--local_counter === 0) {
                    // Increase the "completely finished" counter
                    //  and check if we're finished.
                    if (++internal_counter === total) {
                        BUILD();
                    }
                }
            } // end of fn_detailsCallback
        } // end of fn_searchCallback
    }
    

    函数逻辑在 cmets.我在本节的标题前加上了“复杂”,因为该函数使用了嵌套的局部函数和变量。视觉解释:

    var types, BUILD;
    function search
        var keys, total, internal_counter, fn_searchCallback;
        function fn_searchCallback
            var result, status; // Declared in the formal arguments
            var local_counter, i, fn_detailsCallback;
            function fn_detailsCallback
                var result, status; // Declared in the formal arguments
    

    在上图中,每个缩进级别意味着一个新的scope Explanaation on MDN
    当一个函数被调用 42 次时,就会创建 42 个新的本地作用域,它们共享同一个父作用域。在一个范围内,declared variables 对父范围不可见。虽然父作用域中的变量可以被“子”作用域中的变量读取和更新,前提是你没有声明一个同名的变量。此功能用于我的答案功能。

    【讨论】:

    • searchCallback.apply(this, arguments)是什么意思; ?我不明白这一行 - 我应该在参数中放入什么?默认情况下,searchCallback 接受“结果”和“状态”,但这在 fn_callback 中不可用。
    • @poleapple .apply(this, arguments) 是一种无缝调用函数的通用方式。有关 .apply 方法的说明,请参阅链接文档。如果上下文无关紧要,也可以使用fn_callback = function(a,b){searchCallback(a,b);} 为例。
    • 好的,谢谢!这对我的第一个 searchCallback 非常有用。但是,searchCallback 中的 detailsCallback 直到 searchCallback 完成后才会执行,所以我想我必须做一个双计数器之类的......
    • @poleapple 我没有看到该评论与您当前的代码之间的关系。在您当前的代码中,每个响应都会立即执行 detailsCallback
    • 所以我完全按照您的说明进行操作,在所有 searchCallbacks 完成后调用 BUILD,但是, detailsCallback 仅在 searchCallbacks 完成后才开始执行。所以BUILD现在在detailsCallback执行完成之前执行,但是我需要在detailsCallback完成之后执行BUILD。
    【解决方案2】:

    我想你已经理解了这一点,但是 BUILD() 在之前的回调函数仍在运行时被线性调用。就像您创建了额外的线程一样。解决该问题的一种方法是使 BUILD 成为搜索函数的回调,其中包含 for 循环。这将保证所有功能在调用之前都已完成。

    这个问题可能有助于实现回调:Create a custom callback in JavaScript

    【讨论】:

    • 但是如果我在搜索函数内部执行for循环,然后调用调用BUILD的回调,那不是一回事吗? BUILD 仍然会在 for 循环完成后和 for 循环内的回调完成之前执行。
    • 现在 build 不是搜索函数的回调,它只是循环之后的下一个动作。我最近遇到了一个类似的问题,我的下一行在函数完成之前执行,但是将下一行放在回调中确保它在一切完成时发生。
    猜你喜欢
    • 2017-07-27
    • 1970-01-01
    • 2015-09-12
    • 1970-01-01
    • 2019-03-25
    • 2015-07-02
    • 2014-05-12
    • 1970-01-01
    相关资源
    最近更新 更多