简体   繁体   中英

How to wait for setTimeout before each function call?

I need to call a function multiple times with multiple parameters. I want it to be called after the timeout of the previous call is completed. I've tried the following method but it doesn't seem to be working correctly. ( Here's the JSFiddle ).

Right now it is only waiting after first call. I know it's not the correct way to do this, but I could not find any examples that correctly showed how to do. I will also need to convert it to typescript , so please consider that while answering.

<!DOCTYPE html>
<html>
<body>

<p>Click the button to wait 3 seconds, then alert "Hello".</p>

<button onclick="call()">Call all methods</button>

<script>
var myVar;

function call(){
myFunction('normal1','heading1','message1');
myFunction('normal2','heading2','message2');
myFunction('normal3','heading3','message3');

/*Output should be:
(first time dont wait)
heading1 message1
(then wait for timeout and remove the elements)
heading2 message2
(then wait for timeout and remove the elements)
heading3 message3
*/
}

function myFunction(msgType,heading,message) {
console.log(!!document.getElementById("snackbarParent"),document.getElementById('snackbarParent'));
    if(document.getElementById("snackbarParent") == null)
    {
        alertFunc(msgType,heading,message);
    }
    else{
        setTimeout(function(){
        let parent = document.getElementById('snackbarParent');
            parent.parentNode.removeChild(parent);
            alertFunc(msgType,heading,message);
        },3500);
    }
}

function alertFunc(msgType,heading,message) {

    let div = document.createElement('div');
        div.className = 'snackbarParent';
        div.id = "snackbarParent";
        div.innerHTML = '<div id="snackbar"><b style="color:' + msgType + '"> ' + heading + ' </b>' + message + '</div>';

        document.documentElement.appendChild(div);
        // Get the snackbar DIV
        let x = document.getElementById("snackbar");

        // Add the "show" class to DIV
        x.className = "show";
    setTimeout(function(){
        x.className = x.className.replace("show", "");
        alert("Should display "+heading+" "+message+" now!");
    }, 3000);

}
</script>

</body>
</html>

Note:

The function call() is just for representation purposes. The parameters can have any value and the function myFunction() can be called anytime, anywhere.

Since this question is marked with typescript I'm going to asume that writing this in Typescript is an option.

You could use async/await to easily achieve the effect you want while your code maintains a normal look:

var myVar;

async function call(){
    await myFunction('normal1', 'heading1', 'message1');
    await myFunction('normal2', 'heading2', 'message2');
    await myFunction('normal3', 'heading3', 'message3');
}
function timeout(delay: number) {
    return new Promise(r => setTimeout(r, delay));
}
async function myFunction(msgType: string, heading: string, message: string) {
    console.log(!!document.getElementById("snackbarParent"), document.getElementById('snackbarParent'));
    if (document.getElementById("snackbarParent") == null) {
        await alertFunc(msgType, heading, message);
    }
    else {
        await timeout(3500);

        let parent = document.getElementById('snackbarParent');
        parent.parentNode.removeChild(parent);
        await alertFunc(msgType, heading, message);

    }
}

async function  alertFunc(msgType, heading, message) {

    let div = document.createElement('div');
    div.className = 'snackbarParent';
    div.id = "snackbarParent";
    div.innerHTML = '<div id="snackbar"><b style="color:' + msgType + '"> ' + heading + ' </b>' + message + '</div>';

    document.documentElement.appendChild(div);
    // Get the snackbar DIV
    let x = document.getElementById("snackbar");

    // Add the "show" class to DIV
    x.className = "show";
    await timeout(3000);
    x.className = x.className.replace("show", "");
    alert("Should display " + heading + " " + message + " now!");

}

Note : If you don't want typescript, babel also has support for async/await but you will still need a transpiler.

Note To compile for es5 with async/await you will need a Priomise library if promises aren't in the environment, and you can use the flowing tconfig.json:

"compilerOptions": {
    "target": "es5",
    "lib": [
        "es5",
        "es2015.promise",
        "dom"
    ]
}

A pure js approach could use an onDone callback to have a way to notify the caller of when the function really completed. This is better that to add the code directly in alertFunc as other answers suggest as that will make alertFunc less reusable:

function call() {
    myFunction('normal1', 'heading1', 'message1', function () {
        myFunction('normal2', 'heading2', 'message2', function () {
            myFunction('normal3', 'heading3', 'message3', function () {
                // DOne
            });
        });
    });
}
function myFunction(msgType, heading, message, onDone) {
    console.log(!!document.getElementById("snackbarParent"), document.getElementById('snackbarParent'));
    if (document.getElementById("snackbarParent") == null) {
        alertFunc(msgType, heading, message, onDone);
    }
    else {
        setTimeout(function () {
            var parent = document.getElementById('snackbarParent');
            parent.parentNode.removeChild(parent);
            alertFunc(msgType, heading, message, onDone);
        }, 3500);
    }
}
function alertFunc(msgType, heading, message, onDone) {
    var div = document.createElement('div');
    div.className = 'snackbarParent';
    div.id = "snackbarParent";
    div.innerHTML = '<div id="snackbar"><b style="color:' + msgType + '"> ' + heading + ' </b>' + message + '</div>';
    document.documentElement.appendChild(div);
    // Get the snackbar DIV
    var x = document.getElementById("snackbar");
    // Add the "show" class to DIV
    x.className = "show";
    setTimeout(function () {
        x.className = x.className.replace("show", "");
        alert("Should display " + heading + " " + message + " now!");
        if (onDone)
            onDone();
    }, 3000);
}

 var massages = []; massages[0] = ['normal1','heading1','message1']; massages[1] = ['normal2','heading2','message2']; massages[2] = ['normal3','heading3','message3']; function settime(i){ setTimeout(fn,3000,i) } function fn(i){ var heading = massages[i][1], message = massages[i][2], msgType = massages[i][0]; let x = document.getElementById("snackbar"); x.innerHTML = '<div id="snackbar"><b style="color:' + msgType + '"> ' + heading + ' </b>' + message + '</div>'; alert("Should display "+heading+" "+message+" now!"); if(i<massages.length-1) {i++;settime(i)}; } 
 <p>Click the button to wait 3 seconds, then alert "Hello".</p> <button onclick="settime(0)">Try it</button> <div id='snackbar'></div> 

try this pattern

function callMe(yourParam){
//you can conditionly modify your param 
//if (somecondition)
// newParam=yourPram ... 
setTimeout(()=>callMe(newParam),1000)
}

Actually, it is not nice approach to perform it but in this case, you couldn't call myFunction function consecutively because setTimeout runs asynchronous and don't block the thread. So, modify alertFunc like this;

function alertFunc(msgType,heading,message) {

    let div = document.createElement('div');
        div.className = 'snackbarParent';
        div.id = "snackbarParent";
        div.innerHTML = '<div id="snackbar"><b style="color:' + msgType + '"> ' + heading + ' </b>' + message + '</div>';

        document.documentElement.appendChild(div);
        // Get the snackbar DIV
        let x = document.getElementById("snackbar");

        // Add the "show" class to DIV
        x.className = "show";
    setTimeout(function(){
            if(msgType == "normal1")
            {
                 myFunction('normal2','heading2','message2');//Call second function after first one completed
            }
            if(msgType == "normal2")
            {
                 myFunction('normal3','heading3','message3');//Call third function after second one completed
            }
        x.className = x.className.replace("show", "");
        alert("Should display "+heading+" "+message+" now!");
    }, 3000);

}

And the Call function looks like;

function call(){
   myFunction('normal1','heading1','message1'); //Just call first function in the beginning
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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