używając setTimeout w łańcuchu obietnic


115

Tutaj próbuję zawinąć głowę wokół obietnic, tutaj na pierwsze żądanie pobieram zestaw linków, a na następne żądanie pobieram zawartość pierwszego linku, ale chcę zrobić opóźnienie przed zwróceniem następnego obiektu obietnicy. setTimeout on it.Ale daje mi następujący błąd JSON ( without setTimeout() it works just fine)

Błąd składni: JSON.parse: nieoczekiwany znak w wierszu 1 kolumny 1 danych JSON

chciałbym wiedzieć, dlaczego się nie udaje?

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
Zauważ, że returnjest to specyficzne dla funkcji i wraca tylko do funkcji nadrzędnej i że nie możesz powrócić z metody asynchronicznej.
adeneo

2
Zauważ, że istnieją znacznie lepsze sposoby tworzenia struktury tego kodu niż użycie globalObj.
Bergi

Gdzie JSON.parserzuca? Trudno mi uwierzyć, że to, czy istnieje setTimeoutw jednym thenwywołaniu zwrotnym, wpływa na połączenie w poprzednim thenwywołaniu zwrotnym.
Bergi

Odpowiedzi:


191

Aby utrzymać łańcuch obietnic, nie możesz użyć setTimeout()tego, co zrobiłeś, ponieważ nie zwracasz obietnicy od .then()obsługi - zwracasz ją z setTimeout()wywołania zwrotnego, co nie przynosi ci korzyści.

Zamiast tego możesz wykonać prostą funkcję opóźnienia, taką jak ta:

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

A następnie użyj tego w ten sposób:

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

Tutaj zwracasz obietnicę od przewodnika .then()i dlatego jest ona odpowiednio powiązana.


Możesz również dodać metodę opóźnienia do obiektu Promise, a następnie bezpośrednio użyć .delay(x)metody do swoich obietnic, takiej jak ta:

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

Lub użyj biblioteki obietnic Bluebird, która ma już .delay()wbudowaną metodę.


1
funkcja rozstrzygania jest funkcją wewnątrz then () .. więc setTimeout (rozwiązywanie, t) oznacza setTimeout (funkcja () {powrót ....}, t) czyż nie ... więc dlaczego to zadziała?
AL-zami

2
@ AL-zami - delay()zwraca obietnicę, która zostanie rozwiązana po setTimeout().
jfriend00

Utworzyłem opakowanie obietnicy dla setTimeout, aby łatwo opóźnić obietnicę. github.com/zengfenfei/delay
Kevin

4
@pdem - vto opcjonalna wartość, z którą chcesz, aby obietnica opóźnienia została rozwiązana, a tym samym przekazała łańcuch obietnic. resolve.bind(null, v)jest na miejscu function() {resolve(v);} Albo będzie działać.
jfriend00

dziękuję bardzo ... opóźnienie prototypu zadziałało, ale funkcja >>>. wartość t była nieokreślona.
Christian Matthew,

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

AKTUALIZACJA:

kiedy potrzebuję spać w funkcji asynchronicznej, wrzucam

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

Nie mógłbyś po prostu spać w ramach takiej funkcji asynchronicznej? czekaj na nową obietnicę (rozwiąż => setTimeout (rozwiąż, 1000));
Anthony Moon Beam Toorie

@AnthonyMoonBeamToorie naprawiono, ty
Igor Korsakov

Witaj mój przyjacielu 🧐 Pozdrawiam
Anthony Moon Beam Toorie

52

Krótsza wersja ES6 odpowiedzi:

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

A potem możesz:

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

a jeśli potrzebujesz rejectopcji, np. do walidacji eslint, toconst delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms))
David Thomas

10

Jeśli jesteś wewnątrz bloku .then () i chcesz wykonać 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!!!');

            })

wyjście będzie, jak pokazano poniżej

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

Miłego kodowania!


-1

W node.js możesz również wykonać następujące czynności:

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

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

Próbowałem tego i otrzymałem nieprawidłową liczbę argumentów, oczekiwano 0 w funkcji opóźnienia.
Alex Rindone

Mogę potwierdzić, że działa w node.js 8, 10, 12, 13. Nie jestem pewien, jak uruchamiasz swój kod, ale mogę tylko założyć, że utiljest on nieprawidłowo wypełniany. Używasz pakietu czy czegoś takiego?
Jan
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.