Je veux maintenir l'exécution de nodejs dans setTimeout à l'intérieur de la boucle while. J'ai utilisé la fonction cascade asynchrone mais cela n'a pas fonctionné à l'intérieur de la boucle while. J'ai donc utilisé le code ci-dessous: -

    var i = 3;

    while(i> 0)
    {
        setTimeout(function(){
            console.log('start', i);

            setTimeout(function(){ console.log('timeout');}, 1000);

            console.log('end', i);

            i--;
        }, 1000);

    }
console.log("execution ends");

Mais je n'ai pas obtenu la sortie attendue. Ma sortie attendue sera comme: -

    start3
    timeout
    end3
    start2
    timeout
    end2
    start1
    timeout
    end1
    execution ends
1
Ketan Bodarya 12 avril 2018 à 10:10

6 réponses

Meilleure réponse

Voie 1: utilisez while loop

var i = 3
var p = Promise.resolve(i)
while (i > 0) {
  (i => {
    p = p.then(() => {
      return new Promise(function (resolve, reject) {
        console.log('start', i)
        setTimeout(function () {
          console.log('timeout')
          console.log('end', i)
          resolve()
        }, 1000)
      })
    })
  })(i)
  i--
}
p = p.then(data => console.log('execution ends'))

Way2:

function test(i) {
  console.log('start', i)
  setTimeout(() => {
    console.log('timeout')
    console.log('end', i)
    i--
    if (i < 0) {
      return
    }
    test(i)
  }, 1000)
}
test(3);

Way3: utilisez async

async function test (i) {
  console.log('start', i)
  await new Promise(function (resolve, reject) {
    setTimeout(() => {
      console.log('timeout')
      console.log('end', i)
      i--
      if (i < 0) {
        reject(i)
      }
      resolve(i)
    }, 1000)
  })
    .then(i => test(i))
    .catch(i => console.log('done', i))
}
test(3)
1
xianshenglu 12 avril 2018 à 08:57

Veuillez essayer l'extrait ci-dessous. Vous pouvez introduire des appels d'API OU des appels asynchrones à la place de setTimeout dans la méthode timer.

const timer = () => {
    return new Promise(res => {
        setTimeout(() => {
            console.log('timeout');
            res();
        }, 1000);
    });
}

let i = 3;
let temp;

while (i > 0) {
    if (temp !== i) {
        temp = i;
        console.log('start', temp);
        timer().then(() => {
            console.log('end', temp);
            i -= 1;
        });
    }
}
0
Rushikesh Bharad 12 avril 2018 à 08:17

Nodejs est asynchrone par nature et setTimeout est plus ou moins comme exécuter quelque chose sur un nouveau thread (plus ou moins parce que JS est un thread unique et utilise une boucle d'événement).

Découvrez ce package npm: https://www.npmjs.com/package/sleep

Cela devrait faire l'affaire ...
Mais vous ne devez évidemment utiliser sleep qu'à des fins de débogage. Dans le code de production, vous feriez mieux d'embrasser la nature asynchrone de NodeJS et d'utiliser des promesses

Découvrez ceci pour plus de détails: boucle d'événement: https://developer.mozilla.org/en-US / docs / Web / JavaScript / EventLoop

SetTimeout: https://developer.mozilla.org/en- US / docs / Web / API / WindowOrWorkerGlobalScope / setTimeout

Promesse: https://developer.mozilla.org/ fr-fr / docs / Web / JavaScript / Reference / Global_Objects / Promise

0
ben 12 avril 2018 à 07:18

Il y a quelques problèmes avec votre programme par rapport à votre sortie attendue. Le premier i-- ne fonctionnera qu'après 1000 ms. À ce moment-là, trop de boucles while auraient été exécutées. De même, setTimeOut n'est pas bloquant, par conséquent, le execution ends sera consolé avant même que le premier start ne soit consolé. Pour atteindre votre résultat attendu, vous pouvez apporter quelques modifications au code:

var i = 3;var j =3;

    while(j> 0)
    {
        setTimeout(function(){
            console.log('start', i);
            console.log('timeout');
            console.log('end', i);

            i--;
        }, 1000);
        j--;
    }
    if(j == 0){
        setTimeout(function(){
            console.log('execution ends');
        }, 1000);


    }
0
UchihaItachi 12 avril 2018 à 07:34

Vous n'avez pas obtenu la sortie attendue car il y a une fermeture dans votre code, améliorez votre code comme ceci:

var i = 3;
            while(i> 0)
            {
                setTimeout((
                    function(i){
                        return function(){
                            console.log('start', i);
                            setTimeout(function(){ console.log('timeout');}, 1000);
                            console.log('end', i);
                            i--;
                        }
                    }
                )(i), 1000);
            }
-1
zero 12 avril 2018 à 07:20

Tout d'abord, vous devez comprendre que setTimeout() en Javascript n'est pas bloquant. Cela signifie que tout ce qu'il fait est de planifier l'exécution de quelque chose plus tard, puis le reste de votre code continue immédiatement de s'exécuter.

Dans votre exemple de code particulier, vous aurez une boucle infinie car la boucle while continue indéfiniment, en continuant de planifier de plus en plus de temporisations, mais jusqu'à ce que la boucle while s'arrête, aucune de ces temporisations ne peut s'exécuter. Et, jusqu'à ce que l'un de ces temporisateurs puisse s'exécuter, votre variable de boucle i n'est jamais modifiée, de sorte que la boucle while ne s'arrête jamais.

Pour comprendre pourquoi cela fonctionne de cette façon, vous devez vraiment comprendre la conception événementielle de Javascript et de node.js. Lorsque vous appelez setTimeout(), il planifie un temporisateur interne à l'intérieur du moteur JS. Lorsque ce minuteur se déclenche, il insère un événement dans la file d'attente d'événements Javascript. La prochaine fois que l'interpréteur JS aura terminé ce qu'il faisait, il vérifiera la file d'attente des événements et extraira l'événement suivant de la file d'attente et l'exécutera. Mais, votre boucle while ne s'arrête jamais, elle ne peut donc jamais accéder à de nouveaux événements, elle ne peut donc exécuter aucun de vos événements de minuterie.

Je vais vous montrer trois façons différentes de générer la sortie souhaitée et elles sont toutes dans des extraits de code exécutables afin que vous puissiez les exécuter directement dans la réponse pour voir leurs résultats.

La première technique est accomplie en Javascript simple simplement en réglant des temporisateurs à des moments différents de l'avenir de sorte que les différentes sorties souhaitées se déclenchent dans la bonne séquence:

Minuteries variables

let cntr = 3;
for (let i = 1; i <= 3; i++) {
    // schedule timers at different increasing delays
    setTimeout(function() {
        console.log('start', cntr);
        setTimeout(function() {
            console.log('timeout');
            console.log('end', cntr);
            --cntr;
            if (cntr === 0) {
                console.log("execution ends");
            }
        }, 1000);
    }, i * 2000);
}

Ou, si vous voulez vraiment que ce soit une boucle while:

let cntr = 3, i = 1;
while (i <= 3) {
    // schedule timers at different increasing delays
    setTimeout(function() {
        console.log('start', cntr);
        setTimeout(function() {
            console.log('timeout');
            console.log('end', cntr);
            --cntr;
            if (cntr === 0) {
                console.log("execution ends");
            }
        }, 1000);
    }, i * 2000);
    i++;
}

Utilisation de promesses pour séquencer les opérations

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

function run(i) {
    return delay(1000).then(() => {
        console.log("start", i);
        return delay(1000).then(() => {
            console.log("timeout");
            console.log("end", i);
            return i - 1;
        });
    });
}

run(3).then(run).then(run).then(() => {
    console.log("execution ends");
});

Cela peut également être mis en boucle si vous le souhaitez:

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

function run(i) {
    return delay(1000).then(() => {
      console.log("start", i);
      return delay(1000).then(() => {
          console.log("timeout");
          console.log("end", i);
          return i - 1;
      });
    });
}

[3, 2, 1].reduce((p, v) => {
     return p.then(() => {
         return run(v);
     });
}, Promise.resolve()).then(() => {
    console.log("execution ends");
});

Promises Plus ES7 Async / Await

Cette méthode utilise la fonctionnalité await d'ES7 pour vous permettre d'écrire du code de type séquentiel à l'aide de promesses et await, ce qui peut rendre ces types de boucles beaucoup plus simples. await bloque l'exécution interne d'une fonction pendant qu'elle attend une promesse à résoudre. Il ne bloque pas le retour externe de la fonction. la fonction revient toujours immédiatement. Il renvoie une promesse qui se résout lorsque toutes les pièces bloquées de la fonction interne sont terminées. C'est pourquoi nous utilisons .then() sur le résultat de runSequence() pour savoir quand tout est terminé.

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

async function runSequence(num) {
    for (let i = num; i > 0; i--) {
        await delay(1000);
        console.log("start", i);
        await delay(1000);
        console.log("timeout");
        console.log("end", i);
    }
}

runSequence(3).then(() => {
    console.log("execution ends");
});

Cet exemple await illustre comment les promesses et await peuvent simplifier le séquencement des opérations dans ES7. Mais, ils nécessitent toujours que vous compreniez comment les opérations asynchrones fonctionnent en Javascript, donc n'essayez pas de sauter ce niveau de compréhension en premier.

0
jfriend00 12 avril 2018 à 08:40