简体   繁体   English

用setTimeout递归调用函数

[英]Calling a function recursively with setTimeout

I want call few function one after another recursively with setTimeout . 我想用setTimeout递归地调用几个函数。

var flag = 0 ;
function slave1(){
    if(flag < 60) {
        var COPY_PO_LINE_DIV = document.getElementById("DOM_ELEMENT1"); // Checking if DOM has loaded or not. If yes then doing something.
        if (COPY_PO_LINE_DIV != null) {
            flag = 0;
            //doing something
        } else { 
            setTimeout(slave1,2000); //waiting for 2 seconds and checking again.
        }
    }
}

//doing similar task
function slave2(){
    if(flag < 60) {
        var COPY_PO_LINE_DIV = document.getElementById("DOM_ELEMENT2");              
        if (COPY_PO_LINE_DIV != null) {
            flag = 0;
            //doing something
        } else {
            setTimeout(slave2,2000);
        }
    }
}

function master() {
    slave1();
    console.log("Without completing slave1 function.");
    slave2();
}

Through master() function I want to call multiple functions one after another, however in current situation its calling slave2() without completing slave1() . 我想通过master()函数一个接一个地调用多个函数,但是在当前情况下,它调用slave2()而不完成slave1() How can I make sure that slave1() has executed completed. 我如何确保slave1()已执行完毕。 If DOM element is not loaded than it should execute 60 times after every 2 seconds and than it should come out from slave1() and go to next one. 如果未加载DOM元素,则它应每2秒执行60次,然后应从slave1()取出并转到下一个。

I want to execute same function for 60 times if dom element is not loaded without returning the control to next function. 如果不加载dom元素而不将控件返回到下一个功能,我想执行60次相同的功能。

You need to adjust slave1 to run a callback when it is finished which will be slave2 . 您需要调整slave1以在完成后运行回调,该回调将为slave2

function slave1(callback){
    if(flag < 60) {
        var COPY_PO_LINE_DIV = document.getElementById("DOM_ELEMENT1"); // Checking if DOM has loaded or not. If yes then doing something.
        if (COPY_PO_LINE_DIV != null) {
            flag = 0;
            //doing something
            callback();
        } else { 
            setTimeout(slave1,2000); //waiting for 2 seconds and checking again.
        }
    }
}

function slave2(){...}

function master() {
    slave1(slave2);
    console.log("Without completing slave1 function.");
}

This is your basic javascript chaining. 这是您的基本javascript链接。 If you have more slaves you might want to look into async.series otherwise you go into callback hell as Gabs00 has put it nicely: 如果您有更多的奴隶,您可能想研究async.series否则您会进入回调地狱,因为Gabs00很好地指出了这一点:

slave1(function(){
    slave2(function(){
        slave3(function(){
            slave4(slave5);
        });
    });
});

If you need to pass values to callbacks then you need to use an intermediate anonymous function which in turn calls the intended callback with the arguments in question. 如果您需要将值传递给回调函数,则需要使用一个中间匿名函数,该函数进而使用相关参数调用预期的回调函数。 To do that, you need define your functions so that they use the arguments: 为此,您需要定义函数,以便它们使用参数:

function slave1(str, callback){...}
function slave3(i, callback){...}

slave1("some argument", function(){
    slave2("another argument", function(){
        slave3(1, function(){
            slave4(2, slave5);
        });
    });
});

Consider using promises for things like that. 考虑对此类事情使用诺言。 Here an implementation on top of jQuery, other promise libraries work similarly. 在此基于jQuery的实现,其他promise库的工作方式与此类似。

function waitForElement(elementId, maxTries, checkInterval) {
    var d = $.Deferred(), intvalID, checkFunc;

    // set up default values
    maxTries = maxTries || 60;
    checkInterval = checkInterval || 2000;

    checkFunc = function () {
        var elem = document.getElementById(elementId);
        if (maxTries-- > 0 && elem) {
            clearInterval(intvalID);
            d.resolve(elem);
        }
        if (maxTries <= 0) {
            clearInterval(intvalID);
            d.reject(elementId);
        }
    };

    // set up periodic check & do first check right-away
    intvalID = setInterval(checkFunc, checkInterval);
    checkFunc();

    return d.promise();
}

Now, if you want to test for elements one after another, you can cascade the calls like this: 现在,如果要一个接一个地测试元素,则可以像这样级联调用:

function master() {
    waitForElement("DOM_ELEMENT1").done(function (elem1) {
        waitForElement("DOM_ELEMENT2").done(function (elem2) {
            alert("elem1 and elem2 exist!");
            // now do something with elem1 and elem2
        }).fail(function () {
            alert("elem1 exists, but elem2 was not found.");
        });
    }).fail(function () {
        alert("elem1 not found.");
    });
}

or you can do it in parallel and have a callback called when all of the elements exist: 或者您可以并行执行此操作,并在所有元素都存在时调用回调:

function master() {
    $.when(
        waitForElement("DOM_ELEMENT1"),
        waitForElement("DOM_ELEMENT2")
    )
    .done(function (elem1, elem2) {
        alert("elem1 and elem2 exist!");
        // now do something with elem1 and elem2 
    })
    .fail(function () {
        alert("not all elements were found before the timeout");
    });
}

Your slave2 function should be passed to slave1 function as a callback and should be called in slave1 after it finishes (if ever?). 您的slave2函数应作为回调传递给slave1函数,并应在它完成后在slave1调用(如果有的话?)。 Your current situation is quite common, since setTimeout() function is asynchronous, thus JS interpreter doesn't wait till the function is completed, but sets the setTimeout() result at the end of the Evet Loop and continues processing the master() method. 您的当前情况非常普遍,因为setTimeout()函数是异步的,因此JS解释器不会等到函数完成后才在Evet循环的末尾设置setTimeout()结果并继续处理master()方法。

In order to pass arguments to functions, creating anonymous functions turns out to be an overkill. 为了将参数传递给函数,创建匿名函数实在是太过分了。 Consider using "bind" instead. 考虑改为使用“绑定”。 So, if you've got 所以,如果你有

function slave1(str, callback){...}
function slave2(str, callback){...}
function slave3(i, callback){...}
function slave4(i, callback){...}
function slave5()

Instead of using 而不是使用

slave1("some argument", function(){
    slave2("another argument", function(){
        slave3(1, function(){
            slave4(2, slave5);
        });
    });
});

Consider using 考虑使用

slave1("some argument", 
    slave2.bind(null, "another argument",
        slave3.bind(null, 1,
            slave4.bind(null, 2, slave5)
        )
    )
);

Much easier, more efficient in terms of memory and CPU utilization. 就内存和CPU利用率而言,要容易得多,效率更高。 Now, how to do this with setTimeout: 现在,如何使用setTimeout做到这一点:

slave1("some argument",
    setTimeout.bind(null, slave2.bind(null, "another argument",
        setTimeout.bind(null, slave3.bind(null, 1,
            setTimeout.bind(null, slave4.bind(null, 2,
                setTimeout.bind(null, slave5, 0)
            ),0)
        ),0)
    ),0)
);

I explained the problem in more detail at http://morethanslightly.com/index.php/2014/09/executables-the-standard-solution-aka-mind-the-bind/ 我在http://morethanslightly.com/index.php/2014/09/executables-the-standard-solution-aka-mind-the-bind/上更详细地解释了该问题

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

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