en utilisant setTimeout sur la chaîne de promesse


115

Ici, j'essaye de faire des promesses, ici, à la première demande, je récupère un ensemble de liens, et à la demande suivante, je récupère le contenu du premier lien.Mais je veux faire un délai avant de renvoyer le prochain objet de la promesse. setTimeout dessus, mais cela me donne l'erreur JSON suivante ( without setTimeout() it works just fine)

SyntaxError: JSON.parse: caractère inattendu à la ligne 1 colonne 1 des données JSON

je voudrais savoir pourquoi cela échoue?

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
Notez que cela returnest spécifique à la fonction et renvoie uniquement à la fonction parente, et que vous ne pouvez pas retourner à partir d'une méthode asynchrone.
adeneo le

2
Notez qu'il existe de bien meilleures façons de structurer ce code que d'utiliser un fichier globalObj.
Bergi

JSON.parsejette-t-il? J'ai du mal à croire que le fait qu'il y ait un rappel setTimeoutdans un thenaffecte l'appel dans le thenrappel précédent .
Bergi

Réponses:


191

Pour maintenir la chaîne de promesses en cours, vous ne pouvez pas utiliser setTimeout()la façon dont vous l'avez fait parce que vous ne renvoyez pas une promesse du .then()gestionnaire - vous la renvoyez à partir du setTimeout()rappel, ce qui ne vous sert à rien.

Au lieu de cela, vous pouvez créer une simple petite fonction de retard comme celle-ci:

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

Et puis utilisez-le comme ceci:

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

Ici, vous renvoyez une promesse du .then()gestionnaire et elle est donc enchaînée de manière appropriée.


Vous pouvez également ajouter une méthode de délai à l'objet Promise, puis utiliser directement une .delay(x)méthode sur vos promesses comme celle-ci:

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 utilisez la bibliothèque de promesses Bluebird qui a déjà la .delay()méthode intégrée.


1
La fonction de résolution est la fonction à l'intérieur de then () .. donc setTimeout (résoudre, t) signifie que setTimeout (function () {return ....}, t) n'est-ce pas ... alors pourquoi ça fonctionnera?
AL-zami

2
@ AL-zami - delay()renvoie une promesse qui sera résolue après le setTimeout().
jfriend00

J'ai créé un wrapper de promesse pour setTimeout afin de retarder facilement une promesse. github.com/zengfenfei/delay
Kevin

4
@pdem - vest une valeur facultative avec laquelle vous souhaitez que la promesse de délai se résolve et ainsi transmette la chaîne de promesse. resolve.bind(null, v)est à la place de l'un function() {resolve(v);} ou l'autre fonctionnera.
jfriend00

merci beaucoup ... le retard du prototype a fonctionné mais pas la fonction >>>. le t n'était pas défini.
Christian Matthew

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

METTRE À JOUR:

quand j'ai besoin de dormir dans la fonction asynchrone, je lance

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

Ne pourriez-vous pas simplement dormir dans une fonction asynchrone comme ça? attendre une nouvelle promesse (résoudre => setTimeout (résoudre, 1000));
Anthony Moon Beam Toorie

@AnthonyMoonBeamToorie corrigé, ty
Igor Korsakov

Bienvenue mon ami 🧐 bravo
Anthony Moon Beam Toorie

52

La version ES6 plus courte de la réponse:

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

Et puis vous pouvez faire:

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

et si vous avez besoin de l' rejectoption, par exemple pour la validation eslint, alorsconst delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms))
David Thomas

10

Si vous êtes dans un bloc .then () et que vous voulez exécuter 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 sortie sera comme indiqué ci-dessous

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

Bon codage!


-1

Dans node.js, vous pouvez également effectuer les opérations suivantes:

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

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

J'ai essayé cela et j'ai obtenu un nombre d'arguments invalide, attendu 0 dans la fonction de retard.
Alex Rindone

Je peux confirmer que cela fonctionne dans node.js 8, 10, 12, 13. Je ne sais pas comment vous exécutez votre code mais je ne peux que supposer qu'il utilest polyfilled incorrectement. Utilisez-vous un bundler ou quelque chose?
janvier
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.