utilizzando setTimeout sulla catena di promesse


115

Qui sto cercando di avvolgere la mia testa attorno alle promesse.Qui alla prima richiesta prendo una serie di collegamenti. E alla richiesta successiva prendo il contenuto del primo collegamento.Ma voglio fare un ritardo prima di restituire il prossimo oggetto della promessa. setTimeout su di esso. Ma mi dà il seguente errore JSON ( without setTimeout() it works just fine)

SyntaxError: JSON.parse: carattere imprevisto alla riga 1 colonna 1 dei dati JSON

vorrei sapere perché fallisce?

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
Nota che returnè specifico della funzione e ritorna solo alla funzione genitore e che non puoi tornare da un metodo asincrono.
adeneo

2
Notare che ci sono modi molto migliori per strutturare questo codice piuttosto che usare un file globalObj.
Bergi

Dove JSON.parsebutta? Trovo difficile credere che la presenza setTimeoutdi una thenrichiamata in una influenzi la chiamata nella thenrichiamata precedente .
Bergi

Risposte:


191

Per mantenere la catena di promesse in corso, non puoi usare setTimeout()il modo in cui hai fatto perché non stai restituendo una promessa dal .then()gestore, la stai restituendo dalsetTimeout() richiamata che non ti serve.

Invece, puoi creare una semplice funzione di ritardo come questa:

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

E poi usalo in questo modo:

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");
    });
});

Qui stai restituendo una promessa dal .then()gestore e quindi è concatenata in modo appropriato.


Puoi anche aggiungere un metodo di ritardo all'oggetto Promise e quindi utilizzare direttamente un .delay(x)metodo sulle tue promesse come questo:

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);
});

Oppure, usa la libreria di promesse di Bluebird che ha già il .delay()metodo integrato.


1
la funzione di risoluzione è la funzione all'interno di then () .. quindi setTimeout (risolvere, t) significa setTimeout (funzione () {ritorno ....}, t) non è così ... quindi perché funzionerà?
AL-zami

2
@ AL-zami - delay()restituisce una promessa che verrà risolta dopo il setTimeout().
jfriend00

Ho creato un wrapper di promessa per setTimeout per ritardare facilmente una promessa. github.com/zengfenfei/delay
Kevin

4
@pdem - vè un valore facoltativo con cui desideri che la promessa di ritardo venga risolta e quindi trasmetta la catena della promessa. resolve.bind(null, v)è al posto di function() {resolve(v);} O funzionerà.
jfriend00

grazie mille ... il ritardo del prototipo ha funzionato ma non la funzione >>> .then statement. la t era indefinita.
Christian Matthew,

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

AGGIORNARE:

quando ho bisogno di dormire nella funzione asincrona, lo inserisco

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

Non potresti semplicemente dormire all'interno di una funzione asincrona in questo modo? attende una nuova promessa (risoluzione => setTimeout (risoluzione, 1000));
Anthony Moon Beam Toorie

@AnthonyMoonBeamToorie risolto, ty
Igor Korsakov

Benvenuto amico mio 🧐 applausi
Anthony Moon Beam Toorie

52

La versione ES6 più breve della risposta:

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

E poi puoi fare:

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

e se hai bisogno rejectdell'opzione, ad esempio per la convalida di eslint, alloraconst delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms))
David Thomas

10

Se sei all'interno di un blocco .then () e vuoi eseguire un settimeout ()

            .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!!!');

            })

l'output sarà come mostrato di seguito

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

Buona programmazione!


-1

In node.js puoi anche fare quanto segue:

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

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

Ho provato questo e ho ottenuto un numero di argomenti non valido, previsto 0 all'interno della funzione di ritardo.
Alex Rindone

Posso confermare che funziona in node.js 8, 10, 12, 13. Non sono sicuro di come stai eseguendo il tuo codice, ma posso solo presumere che utilsia stato polyfilled in modo errato. Stai usando un bundler o qualcosa del genere?
gennaio
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.