Ist es möglich, setTimeout-Funktionen in JavaScript zu verketten?

Lesezeit: 4 Minuten

Benutzer-Avatar
xiatica

Ist eine Verkettung möglich setTimout Funktionen, um sicherzustellen, dass sie nacheinander ablaufen?

Benutzer-Avatar
jfriend00

Hier sind drei separate Ansätze aufgeführt:

  1. Manuell verschachteln setTimeout() Rückrufe.
  2. Verwenden Sie ein verkettbares Timer-Objekt.
  3. Wickeln setTimeout() in einem Versprechen und Kettenversprechen.

SetTimeout-Callbacks manuell verschachteln

Natürlich. Wenn der erste feuert, stellen Sie einfach den nächsten ein.

setTimeout(function() {
    // do something
    setTimeout(function() {
        // do second thing
    }, 1000);
}, 1000);

Verkettbares Timer-Objekt

Sie können sich auch ein kleines Hilfsobjekt erstellen, mit dem Sie Dinge buchstäblich verketten können, mit denen Sie Aufrufe wie folgt verketten können:

delay(fn1, 400).delay(fn2, 500).delay(fn3, 800);

function delay(fn, t) {
    // private instance variables
    var queue = [], self, timer;
    
    function schedule(fn, t) {
        timer = setTimeout(function() {
            timer = null;
            fn();
            if (queue.length) {
                var item = queue.shift();
                schedule(item.fn, item.t);
            }
        }, t);            
    }
    self = {
        delay: function(fn, t) {
            // if already queuing things or running a timer, 
            //   then just add to the queue
        	  if (queue.length || timer) {
                queue.push({fn: fn, t: t});
            } else {
                // no queue or timer yet, so schedule the timer
                schedule(fn, t);
            }
            return self;
        },
        cancel: function() {
            clearTimeout(timer);
            queue = [];
            return self;
        }
    };
    return self.delay(fn, t);
}

function log(args) {
    var str = "";
    for (var i = 0; i < arguments.length; i++) {
        if (typeof arguments[i] === "object") {
            str += JSON.stringify(arguments[i]);
        } else {
            str += arguments[i];
        }
    }
    var div = document.createElement("div");
    div.innerHTML = str;
    var target = log.id ? document.getElementById(log.id) : document.body;
    target.appendChild(div);
}


function log1() {
	  log("Message 1");
}
function log2() {
	  log("Message 2");
}
function log3() {
	  log("Message 3");
}

var d = delay(log1, 500)
    .delay(log2, 700)
    .delay(log3, 600)

Packen Sie setTimeout in ein Promise und verketten Sie Promises

Oder, da es jetzt das Zeitalter der Versprechungen in ES6+ ist, hier ist ein ähnlicher Code, der Versprechungen verwendet, wobei wir die Versprechungsinfrastruktur das Einreihen und Sequenzieren für uns erledigen lassen. Sie können mit einer Verwendung wie dieser enden:

Promise.delay(fn1, 500).delay(fn2, 700).delay(fn3, 600);

Hier ist der Code dahinter:

// utility function for returning a promise that resolves after a delay
function delay
    return new Promise(function (resolve) {
        setTimeout(resolve, t);
    });
}

Promise.delay = function (fn, t) {
    // fn is an optional argument
    if (!t) {
        t = fn;
        fn = function () {};
    }
    return delay
}

Promise.prototype.delay = function (fn, t) {
    // return chained promise
    return this.then(function () {
        return Promise.delay(fn, t);
    });

}

function log(args) {
    var str = "";
    for (var i = 0; i < arguments.length; i++) {
        if (typeof arguments[i] === "object") {
            str += JSON.stringify(arguments[i]);
        } else {
            str += arguments[i];
        }
    }
    var div = document.createElement("div");
    div.innerHTML = str;
    var target = log.id ? document.getElementById(log.id) : document.body;
    target.appendChild(div);
}

function log1() {
    log("Message 1");
}

function log2() {
    log("Message 2");
}

function log3() {
    log("Message 3");
}

Promise.delay(log1, 500).delay(log2, 700).delay(log3, 600);

Die Funktionen, die Sie dieser Version bereitstellen, können entweder synchron oder asynchron sein (Zurückgeben eines Versprechens).

  • Ich entschuldige mich für meine dummen Fragen, aber gibt es Internetbrowser, die Javascript-Funktionen gleichzeitig verarbeiten (einschließlich Mobilgeräte), und wenn ja, stellt dies sicher, dass sie nicht gleichzeitig ausgeführt werden?

    – xiatica

    4. August 2011 um 1:53 Uhr

  • Das gesamte Browser-Javascript ist Single-Threaded. Es wird immer nur ein Ausführungsthread ausgeführt, sodass sequentielle Anweisungen immer nacheinander ausgeführt werden. Zwei Teile von Javascript laufen NIEMALS gleichzeitig.

    – jfriend00

    4. August 2011 um 2:02 Uhr

  • Ein verkettbares Timer-Objekt mit Warteschlangen und einer Cancel-Methode hinzugefügt.

    – jfriend00

    11. Februar 2016 um 22:46 Uhr

  • Kann das fn-Argument von delay eine Funktion sein, die ein Argument wie function logMessage(message) { log(message); }? Und wenn ja, wie könnte ich das Nachrichtenargument übergeben?

    – Ebbraun

    9. Juni 2016 um 6:26 Uhr

  • Danke, bind ist süß und funktioniert. Für alle Interessierten hier jsfiddle.net/efbbrown/ypa25gsc/1 So können Sie Eingaben an das fn-Argument binden.

    – Ebbraun

    9. Juni 2016 um 8:30 Uhr


Inspiriert von @jfriend00 habe ich eine kürzere Version demonstriert:

Promise.resolve()
  .then(() => delay(400))
  .then(() => log1())
  .then(() => delay(500))
  .then(() => log2())
  .then(() => delay(800))
  .then(() => log3());

function delay(duration) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(), duration);
  });
}

function log1() {
  console.log("Message 1");
}

function log2() {
  console.log("Message 2");
}

function log3() {
  console.log("Message 3");
}

Wenn Sie Typescript für ES6 verwenden, ist dies mit Async Await ziemlich einfach. Dies ist auch sehr sehr einfach zu lesen und ein kleines Upgrade auf die Antwortversprechen.

//WARNING: this is Typescript source code
//expect to be async
async function timePush(...arr){
    function delay
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve();
            },t)
        })
    }
    //for the length of this array run a delay
    //then log, you could always use a callback here
    for(let i of arr){
        //pass the items delay to delay function
        await delay(i.time);
        console.log(i.text)
    }
}


timePush(
    {time:1000,text:'hey'},
    {time:5000,text:'you'},
    {time:1000,text:'guys'}
);

Benutzer-Avatar
Kenji

Ich bin auf das gleiche Problem gestoßen. Meine Lösung war, sich selbst anzurufen setTimeoutEs klappt.

let a = [[20,1000],[25,5000],[30,2000],[35,4000]];

function test(){
  let b = a.shift();
  console.log(b[0]);
  if(a.length == 0) return;
  setTimeout(test,b[1]);
}

das zweite Element in Array a ist die zu verzögernde Zeit

Verwenden async / await mit @Penny Liu Beispiel:

(async() => {
  await delay(400)
  log1()
  await delay(500)
  log2()
  await delay(800)
  log3()
})()

async function delay(duration) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(), duration);
  });
}

function log1() {
  console.log("Message 1");
}

function log2() {
  console.log("Message 2");
}

function log3() {
  console.log("Message 3");
}

1009910cookie-checkIst es möglich, setTimeout-Funktionen in JavaScript zu verketten?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy