usando setTimeout na cadeia de promessa


115

Aqui estou eu tentando envolver minha cabeça em torno de promessas. Aqui, na primeira solicitação, eu busco um conjunto de links. E na próxima solicitação, busco o conteúdo do primeiro link. Mas eu quero atrasar antes de retornar o próximo objeto de promessa. setTimeout nele. Mas me dá o seguinte erro JSON ( without setTimeout() it works just fine)

SyntaxError: JSON.parse: caractere inesperado na linha 1 coluna 1 dos dados JSON

gostaria de saber por que falha?

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
Observe que returné específico da função e retorna apenas para a função pai, e que você não pode retornar de um método assíncrono.
adeneo

2
Observe que há maneiras muito melhores de estruturar esse código do que usar um globalObj.
Bergi,

Onde JSON.parsejoga? Acho difícil acreditar que a existência de setTimeoutum thenretorno de chamada afeta a chamada do thenretorno de chamada anterior .
Bergi

Respostas:


191

Para manter a cadeia de promessas em andamento, você não pode usar setTimeout()a maneira como fez porque não está retornando uma promessa do .then()manipulador - você a está retornando do setTimeout()retorno de chamada, o que não adianta nada.

Em vez disso, você pode fazer um pequeno atraso simples funcionar como este:

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

E, em seguida, use-o assim:

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

Aqui, você está devolvendo uma promessa do .then()manipulador e, portanto, ela é devidamente encadeada.


Você também pode adicionar um método de atraso ao objeto Promise e usar diretamente um .delay(x)método em suas promessas como este:

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

Ou use a biblioteca de promessa Bluebird, que já possui o .delay()método integrado.


1
a função de resolução é a função dentro de then () .. então setTimeout (resolve, t) significa que setTimeout (function () {return ....}, t) não é ... então por que funcionará?
AL-zami

2
@ AL-zami - delay()retorna uma promessa que será resolvida após o setTimeout().
jfriend00

Eu criei um wrapper de promessa para setTimeout para atrasar facilmente uma promessa. github.com/zengfenfei/delay
Kevin

4
@pdem - vé um valor opcional com o qual você deseja que a promessa de atraso resolva e, assim, transmita a cadeia de promessa. resolve.bind(null, v)está no lugar de function() {resolve(v);} Qualquer funcionará.
jfriend00

muito obrigado ... o atraso do protótipo funcionou, mas não a função >>>. instrução então. o t era indefinido.
Christian Matthew

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

ATUALIZAR:

quando eu preciso dormir na função assíncrona eu coloco

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

Você não poderia simplesmente dormir em uma função assíncrona como esta? aguarda uma nova promessa (resolve => setTimeout (resolve, 1000));
Anthony Moon Beam Toorie

@AnthonyMoonBeamToorie corrigido, ty
Igor Korsakov

52

A versão ES6 mais curta da resposta:

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

E então você pode fazer:

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

e se você precisar da rejectopção, por exemplo, para validação eslint, entãoconst delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms))
David Thomas

10

Se você estiver dentro de um .then () bloco e você quer executar um 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!!!');

            })

a saída será como mostrado abaixo

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

Happy Coding!


-1

Em node.js, você também pode fazer o seguinte:

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

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

Eu tentei isso e obtive um número inválido de argumentos, esperado 0 dentro da função de atraso.
Alex Rindone

Posso confirmar que funciona em node.js 8, 10, 12, 13. Não tenho certeza de como você está executando seu código, mas só posso assumir que utilestá sendo polyfilled incorretamente. Você está usando um bundler ou algo assim?
janeiro
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.