Verwenden von setTimeout für die Versprechen-Kette


115

Hier versuche ich, meinen Kopf um Versprechen zu wickeln. Hier hole ich auf erste Anfrage eine Reihe von Links. Und auf nächste Anfrage hole ich den Inhalt des ersten Links. Aber ich möchte eine Verzögerung machen, bevor ich das nächste Versprechen zurückschicke. Also benutze ich setTimeout drauf. Aber es gibt mir den folgenden JSON-Fehler ( without setTimeout() it works just fine)

SyntaxError: JSON.parse: Unerwartetes Zeichen in Zeile 1, Spalte 1 der JSON-Daten

Ich würde gerne wissen, warum es fehlschlägt.

let globalObj={};
function getLinks(url){
    return new Promise(function(resolve,reject){

       let http = new XMLHttpRequest();
       http.onreadystatechange = function(){
            if(http.readyState == 4){
              if(http.status == 200){
                resolve(http.response);
              }else{
                reject(new Error());
              }
            }           
       }
       http.open("GET",url,true);
       http.send();
    });
}

getLinks('links.txt').then(function(links){
    let all_links = (JSON.parse(links));
    globalObj=all_links;

    return getLinks(globalObj["one"]+".txt");

}).then(function(topic){


    writeToBody(topic);
    setTimeout(function(){
         return getLinks(globalObj["two"]+".txt"); // without setTimeout it works fine 
         },1000);
});

1
Beachten Sie, dass dies returnfunktionsspezifisch ist und nur zur übergeordneten Funktion zurückkehrt und dass Sie nicht von einer asynchronen Methode zurückkehren können.
Adeneo

2
Beachten Sie, dass es viel bessere Möglichkeiten gibt , diesen Code zu strukturieren, als a zu verwenden globalObj.
Bergi

Wo JSON.parsewirft? Es fällt mir schwer zu glauben, dass die Frage, ob setTimeoutein thenRückruf vorliegt, den Anruf im vorherigen thenRückruf beeinflusst.
Bergi

Antworten:


191

Um die Versprechenskette am Laufen zu halten, können Sie nicht so vorgehen, setTimeout()wie Sie es getan haben, weil Sie kein Versprechen vom .then()Handler zurückgeben - Sie geben es vom setTimeout()Rückruf zurück, was Ihnen nicht gut tut.

Stattdessen können Sie eine einfache kleine Verzögerungsfunktion wie folgt erstellen:

function delay(t, v) {
   return new Promise(function(resolve) { 
       setTimeout(resolve.bind(null, v), t)
   });
}

Und dann benutze es so:

getLinks('links.txt').then(function(links){
    let all_links = (JSON.parse(links));
    globalObj=all_links;

    return getLinks(globalObj["one"]+".txt");

}).then(function(topic){
    writeToBody(topic);
    // return a promise here that will be chained to prior promise
    return delay(1000).then(function() {
        return getLinks(globalObj["two"]+".txt");
    });
});

Hier geben Sie ein Versprechen vom .then()Handler zurück und es ist entsprechend verkettet.


Sie können dem Promise-Objekt auch eine Verzögerungsmethode hinzufügen und dann direkt eine .delay(x)Methode für Ihre Versprechen verwenden:

function delay(t, v) {
   return new Promise(function(resolve) { 
       setTimeout(resolve.bind(null, v), t)
   });
}

Promise.prototype.delay = function(t) {
    return this.then(function(v) {
        return delay(t, v);
    });
}


Promise.resolve("hello").delay(500).then(function(v) {
    console.log(v);
});

Oder verwenden Sie die Bluebird-Versprechensbibliothek, in die die .delay()Methode bereits integriert ist .


1
Die Auflösungsfunktion ist die Funktion in then (). setTimeout (auflösen, t) bedeutet also, dass setTimeout (function () {return ....}, t) nicht wahr ist. Warum funktioniert es also?
AL-Zami

2
@ AL-zami - delay()gibt ein Versprechen zurück, das nach dem gelöst wird setTimeout().
jfriend00

Ich habe einen Versprechens-Wrapper für setTimeout erstellt, um ein Versprechen leicht zu verzögern. github.com/zengfenfei/delay
Kevin

4
@pdem - vist ein optionaler Wert, mit dem das Verzögerungsversprechen aufgelöst und damit die Versprechenskette weitergegeben werden soll. resolve.bind(null, v)ist anstelle von function() {resolve(v);} Entweder wird funktionieren.
jfriend00

Vielen Dank ... die Verzögerung des Prototyps hat funktioniert, aber nicht die Funktion >>> .then Anweisung. das t war undefiniert.
Christian Matthew

74
.then(() => new Promise((resolve) => setTimeout(resolve, 15000)))

AKTUALISIEREN:

Wenn ich in einer asynchronen Funktion schlafen muss, werfe ich ein

await (async () => new Promise(resolve => setTimeout(resolve, 1000)))();

Könnten Sie nicht einfach so in einer asynchronen Funktion schlafen? Warten Sie auf ein neues Versprechen (Entschlossenheit => setTimeout (Auflösung, 1000)).
Anthony Moon Beam Toorie

52

Die kürzere ES6-Version der Antwort:

const delay = t => new Promise(resolve => setTimeout(resolve, t));

Und dann können Sie tun:

delay(3000).then(() => console.log('Hello'));

und wenn Sie die rejectOption benötigen , z. B. für die Eslint-Validierung, dannconst delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms))
David Thomas

10

Wenn Sie sich in einem .then () -Block befinden und ein Settimeout () ausführen möchten

            .then(() => {
                console.log('wait for 10 seconds . . . . ');
                return new Promise(function(resolve, reject) { 
                    setTimeout(() => {
                        console.log('10 seconds Timer expired!!!');
                        resolve();
                    }, 10000)
                });
            })
            .then(() => {
                console.log('promise resolved!!!');

            })

Die Ausgabe erfolgt wie unten gezeigt

wait for 10 seconds . . . .
10 seconds Timer expired!!!
promise resolved!!!

Viel Spaß beim Codieren!


-1

In node.js können Sie auch Folgendes tun:

const { promisify } = require('util')
const delay = promisify(setTimeout)

delay(1000).then(() => console.log('hello'))

Ich habe dies versucht und eine ungültige Anzahl von Argumenten erhalten, 0 innerhalb der Verzögerungsfunktion erwartet.
Alex Rindone

Ich kann bestätigen, dass es in node.js 8, 10, 12, 13 funktioniert. Ich bin mir nicht sicher, wie Sie Ihren Code ausführen, aber ich kann nur davon ausgehen, utildass er falsch ausgefüllt wird. Verwenden Sie einen Bundler oder so?
Januar
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.