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);
});
AL-zami
la source
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.

jfriend00
la source
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))
Igor Korsakov
la source
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'));
Sébastien Rosset
la source
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!

AnoopGoudar
la source
-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'))
Jan
la source
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