简体   繁体   English

我如何推迟这个javascript电话?

[英]How would I defer this javascript call?

I'm trying to figure out how to accomplish this workflow, but can't seem to nail it. 我正在试图弄清楚如何完成这个工作流程,但似乎无法确定它。 I've got n number of <select> elements on a page. 我在页面上有n<select>元素。 When the page loads, for each <select> element, I need to make a $.get(...); 当页面加载时,对于每个<select>元素,我需要创建一个$.get(...); call. 呼叫。 Once all of those calls are done, then, and only then do I need to run an additional function. 一旦完成所有这些调用 ,那么,只有这样我才需要运行其他功能。 Here is some example code to better explain: 以下是一些示例代码,可以更好地解释:

function doWork(selectEl) {
    var getData = ...; // build request data based on selectEl

    $.get('/foo/bar', getData, function (data) {
        // Do something to selectEl with the result
    });
}

function doMoreWork() {
    // Do something with all the selects now that they are ready
}

$(function () {
    // For each of the select elements on the page
    $('select').each(function(index, selectEl) {
        // Go do some AJAX-fetching of additional data
        doWork(selectEl);
    });

    // Once *all* the $.get(...) calls are done, do more things
    doMoreWork();
});

Using the code above, doMoreWork() is usually called before all of the async $.get(...); 使用上面的代码, doMoreWork()通常在所有async $.get(...);之前调用$.get(...); calls have had a chance to return; 电话有机会返回; which is not what I want. 这不是我想要的。 I need to have all of the $.get(...); 我需要拥有所有 $.get(...); calls complete before doMoreWork() can be called. 在调用doMoreWork()之前调用完成。 Basically I need a callback of sorts to execute once ALL of the $.get(...); 基本上我需要一个回调来执行一次所有的$.get(...); calls in the above example have finished. 上面例子中的调用已经完成。

How would I go about accomplishing this? 我将如何实现这一目标?

  1. Every time you call doWork , increment a counter. 每次调用doWork ,都会增加一个计数器。

  2. Every time a response comes back, decrement the counter. 每当响应回来时,减少计数器。

  3. Have the callback invoke doMoreWork when the counter reaches 0 . 当计数器达到0时,让回调调用doMoreWork

var counter = 0;

function doWork(selectEl) {
    counter++;
    var getData = ...; // build request data based on selectEl

    $.get('/foo/bar', getData, function (data) {
        counter--;
        if( !counter ) { doMoreWork(); }
    });
}

function doMoreWork() {
    // Do something with all the selects now that they are ready
}

$(function () {
    // For each of the select elements on the page
    $('select').each(function(index, selectEl) {
        // Go do some AJAX-fetching of additional data
        doWork(selectEl);
    });
});

I would write a class something like: 我会写一个类似的东西:

function synchronizer(query, action, cleanup) {
    this.query = query;
    this.action = action;
    this.cleanup = cleanup;
    this.remaining = query.length;
    this.complete = function() {
        this.remaining -= 1;
        if (this.remaining == 0) { this.cleanup(query); }
    }
    this.run = function() {
        query.each(function(index, which) { action(which, this.complete); })
    }
}

// Aargh. Expecting doWork() to call a passed-in continuation seems ugly to me
// as opposed to somehow wrapping doWork within the synchronizer... but I can't
// think of a way to make that work.

function doWork(element, next) {
    var getData = ...; // build request data based on element

    $.get('/foo/bar', getData, function(data) {
        // Do something to element with the result, and then
        next();
    });
}

function doMoreWork(elements) {
    // Do something with all the selects now that they are ready
}

new synchronizer($('select'), doWork, doMoreWork).run();

Keep track of how many Ajax calls have yet to complete, and execute doMoreWork() when there are none left. 跟踪尚未完成的Ajax调用次数,并在没有剩余时执行doMoreWork()

$(function(){
    var workLeft = $('select').length;

    function doWork(selectEl) {
        var getData = ...; // build request data based on selectEl

        $.get('/foo/bar', getData, function (data) {
            // Do something to selectEl with the result

            // If done all work
            if(!(--workLeft)){
                doMoreWork();
            }                
        });
    }

    function doMoreWork() {
        // Do something with all the selects now that they are ready
    }

    // For each of the select elements on the page
    $('select').each(function(index, selectEl) {
        // Go do some AJAX-fetching of additional data
        doWork(selectEl);
    });
});

You may also want to catch ajax errors. 您可能还想捕获ajax错误。

You can use jQuery's $.when to join together multiple Deferred objects to one: 您可以使用jQuery的$.when将多个Deferred对象连接到一个:

$.when.apply($, $('select').map(function(index, selectEl) {
    return $.ajax(....);
}).get()).done(function() {
    // All AJAX calls finished
});

Basically, $.when takes multiple Deferred objects as each argument and wraps them together as one Deferred by keeping track of the number of completed sub-deferres, similar to how a couple of the answers here implemented it manually. 基本上, $.when将多个Deferred对象作为每个参数并将它们作为一个包装在一起通过跟踪已完成的子延迟的数量来延迟,类似于这里的几个答案如何手动实现它。

A more readable version of the above code is: 以上代码的更易读的版本是:

var requests = [];
$('select').each(function(index, selectEl) {
    request.push($.ajax(....));
}
$.when.apply($, requests).done(function() {
    // All AJAX calls finished
});

Maybe you could use the JavaScript underscore library's after function . 也许你可以使用JavaScript下划线库的after函数

(note: I haven't tested this code) (注意:我没有测试过这段代码)

var numberOfSelectElements = n;
var finished = _after(numberOfSelectElements, doMoreWork);

function doWork(selectEl) {
    var getData = ...; // build request data based on selectEl

    $.get('/foo/bar', getData, function (data) {
        finished();
    });
}

function doMoreWork() {
    // Do something with all the selects now that they are ready
}

$(function () {
    // For each of the select elements on the page
    $('select').each(function(index, selectEl) {
        // Go do some AJAX-fetching of additional data
        doWork(selectEl);
    });
});

Use Deferred: 使用延期:

function doWork(selectEl) {
    var getData = ...;

    // return Deferred object
    return $.get('/foo/bar', getData, function (data) {

    });
}


var selects = $('select');

function doItem(i) {
    if(selects.length === i) return doMoreWork(); // if no selects left, abort and do more work
    $.when(doWork(selects.get(i)).then(function() { // fetch and do next if completed
        doItem(i + 1);
    });
});

doItem(0); // start process

Since it looks like you're doing jQuery, you could use the $.ajaxStop event handler... http://api.jquery.com/ajaxStop/ 既然看起来你在做jQuery,你可以使用$ .ajaxStop事件处理程序...... http://api.jquery.com/ajaxStop/

EDIT Said $.ajaxComplete instead of the correct $.ajaxStop... Fixed now... 编辑说$ .ajaxComplete而不是正确的$ .ajaxStop ...现在修复...

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

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