usando setTimeout en la cadena de promesas

115

Aquí estoy tratando de entender las promesas. Aquí, en la primera solicitud, obtengo un conjunto de enlaces. Y en la siguiente solicitud, obtengo el contenido del primer enlace. Pero quiero hacer un retraso antes de devolver el siguiente objeto de promesa. Así que uso setTimeout en él. Pero me da el siguiente error JSON ( without setTimeout() it works just fine)

SyntaxError: JSON.parse: carácter inesperado en la línea 1, columna 1 de los datos JSON

me gustaría saber por qué falla?

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);
});
AL-zami
fuente
1
Tenga en cuenta que returnes específica de la función y solo regresa a la función principal, y que no puede regresar de un método asincrónico.
adeneo
2
Observe que hay formas mucho mejores de estructurar este código que usar un globalObj.
Bergi
¿Dónde JSON.parsetira? Me resulta difícil de creer que si hay setTimeoutuna thendevolución de llamada en uno afecta la llamada en la thendevolución de llamada anterior .
Bergi

Respuestas:

191

Para mantener la cadena de promesas en marcha, no puede usar setTimeout()la forma en que lo hizo porque no está devolviendo una promesa del .then()controlador, la está devolviendo desde la setTimeout()devolución de llamada, lo que no le sirve de nada.

En su lugar, puede hacer una pequeña función de retraso simple como esta:

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

Y luego úsalo así:

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

Aquí está devolviendo una promesa del .then()controlador y, por lo tanto, está encadenada adecuadamente.


También puede agregar un método de retraso al objeto Promise y luego usar directamente un .delay(x)método en sus promesas 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);
});

O use la biblioteca de promesas Bluebird que ya tiene el .delay()método incorporado.

jfriend00
fuente
1
La función resolve es la función dentro de then () .. entonces setTimeout (resolve, t) significa setTimeout (function () {return ....}, t) ¿no es así ... entonces por qué funcionará?
AL-zami
2
@ AL-zami: delay()devuelve una promesa que se resolverá después de setTimeout().
jfriend00
He creado un contenedor de promesa para setTimeout para retrasar fácilmente una promesa. github.com/zengfenfei/delay
Kevin
4
@pdem: ves un valor opcional con el que le gustaría que la promesa de demora se resolviera y, por lo tanto, pasara a la cadena de promesas. resolve.bind(null, v)está en lugar de function() {resolve(v);} Either will work.
jfriend00
muchas gracias ... el retraso del prototipo funcionó pero no la función >>> .entonces declaración. el t estaba indefinido.
Christian Matthew
76
.then(() => new Promise((resolve) => setTimeout(resolve, 15000)))

ACTUALIZAR:

cuando necesito dormir en la función asincrónica, agrego

await new Promise(resolve => setTimeout(resolve, 1000))
Igor Korsakov
fuente
¿No podrías dormir dentro de una función asincrónica como esa? aguardar nueva promesa (resolver => setTimeout (resolver, 1000));
Anthony Moon Beam Toorie
@AnthonyMoonBeamToorie arreglado, ty
Igor Korsakov
Bienvenido mi amigo 🧐 salud
Anthony Moon Beam Toorie
52

La versión ES6 más corta de la respuesta:

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

Y luego puedes hacer:

delay(3000).then(() => console.log('Hello'));
Sébastien Rosset
fuente
y si necesita la rejectopción, por ejemplo, para la validación de eslint, entoncesconst delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms))
David Thomas
10

Si está dentro de un bloque .then () y desea ejecutar 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!!!');

            })

la salida será como se muestra a continuación

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

¡Feliz codificación!

AnoopGoudar
fuente
-1

En node.js también puede hacer lo siguiente:

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

delay(1000).then(() => console.log('hello'))
ene
fuente
Intenté esto y obtuve un número inválido de argumentos, se esperaba 0 dentro de la función de retraso.
Alex Rindone
Puedo confirmar que funciona en node.js 8, 10, 12, 13. No estoy seguro de cómo está ejecutando su código, pero solo puedo asumir que utilse está rellenando incorrectamente. ¿Estás usando un paquete o algo así?
Ene