Hello. I am writing code using Promises to run functions in sequence. Only a couple of days I deal with them to improve the code (for there used to be a Christmas tree from callbacks); So here. There is a function for animating text output to the Animal () screen:

function Animal(string) { var a = ''; //variable which will be entered character by character string var i= 0;//Letter counter var p = document.createElement('p'); $('body').scrollTop($('body').append(p).height()); return new Promise(function (resolve) { Anima(); function Anima() {//Function of Animation a+=string[i]; i++; $('p').last().text(a); var timer = setTimeout(Anima, 100); if(i==string.length){ clearTimeout(timer); resolve(); }; }; }); }; 

And the AnimalPause () function for the pause image (a line of dots is displayed on the screen and is immediately deleted):

 function AnimalPause(string) { var a = ''; //variable which will be entered character by character string var i= 0;//Letter counter var p = document.createElement('p'); $('body').scrollTop($('body').append(p).height()); return promise = new Promise(function (resolve) { Anima(); function Anima() {//Function of Animation a+=string[i]; i++; $('p').last().text(a); var timer = setTimeout(Anima, 100); if(i==string.length){ clearTimeout(timer); $('p').last().remove(); resolve(); }; }; }); }; 

He did everything, it seems. And, if you output consecutive lines through .then (), everything works. And, if you output the lines sequentially using a loop, everything also works:

 Animal('..........') .then(() => Animal('---Hello! CONSOLE v 1.0.1 is working!---')) .then(() => Animal('To see all commands u can use type -help')) .then(() => Animal('DONE')) 

or so:

 var chain = Promise.resolve(); pause.forEach(function(txt){ chain = chain.then(() => AnimalPause(txt))}); var pause = [ '...', '.....', '....', '.........' ]; 

But if through .then () Sequentially output the text, then the delay, then the text, collapses: (The delay is displayed after the text is displayed):

 function Hello() { var chain = Animal('..........') .then(() => Animal('---Hello! CONSOLE v 1.0.1 is working!---')) .then(() => Animal('To see all commands u can use type -help')) .then(function() { return Pause(chain); // pause.forEach(function(txt){ // chain = chain.then(() => AnimalPause(txt))}) }) .then(() => Animal('DONE')) }; Hello(); function Pause (chain) { return new Promise(function(resolve) { pause.forEach(function(txt){ chain = chain.then(() => AnimalPause(txt))}); resolve(); }) } 

Why? Well .. and how to fix it? :)

On the advice of @Grundy, in the comments I take off the code for testing

 var pause = [ '...', '.....', '....', '.........' ]; function Animal(string) { var a = ''; //variable which will be entered character by character string var i = 0; //Letter counter var p = document.createElement('p'); $('body').scrollTop($('body').append(p).height()); return new Promise(function(resolve) { Anima(); function Anima() { //Function of Animation a += string[i]; i++; $('p').last().text(a); var timer = setTimeout(Anima, 100); if (i == string.length) { clearTimeout(timer); resolve(); }; }; }); }; function AnimalPause(string) { var a = ''; //variable which will be entered character by character string var i = 0; //Letter counter var p = document.createElement('p'); $('body').scrollTop($('body').append(p).height()); return promise = new Promise(function(resolve) { Anima(); function Anima() { //Function of Animation a += string[i]; i++; $('p').last().text(a); var timer = setTimeout(Anima, 100); if (i == string.length) { clearTimeout(timer); $('p').last().remove(); resolve(); }; }; }); }; function Pause(chain) { return new Promise(function(resolve) { pause.forEach(function(txt) { chain = chain.then(() => AnimalPause(txt)) }); resolve(); }) }; function Hello() { var chain = Animal('..........') .then(() => Animal('---Hello! CONSOLE v 1.0.1 is working!---')) .then(() => Animal('To see all commands u can use type -help')) .then(function() { return Pause(chain); //Вы заметите, что строка пауз выводится после вывода на экран строки "DONE". Должно быть наоборот }) .then(() => Animal('DONE')) }; Hello(); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 

  • somehow everything is too complicated. instead of forEach in this case it’s better to use reduce . What kind of pause ? I found Grundy
  • The code is terrible. Break it into small tasks and check which one of them does not work. For such tasks it is very convenient to use generators. - user220409
  • As I understand it, for some reason, the promises that are inside the array (ignored) are transferred to the end of the whole chain (mb because of the assignment of the chain ..), but although not, it seems. When I consistently wedged it in without using a function, but directly inserting an array, it seemed to be performed separately from all promises and the subsequent text was displayed on top of the pause. - mayst
  • @mayst, make a minimal reproducible example so that you can run and see the problem - Grundy
  • @Grundy, Ie to throw a code into the question that you could copy yourself and test? - mayst

1 answer 1

If you walk through the code, it can be noted that the Animal function differs from AnimalPause only in the fact that in the latter the added element is eventually removed.

Based on this, you can pass the created p to resolve to the Animal function and delete it if necessary. AnimalPause will degenerate into the next.

 function AnimalPause(string) { return Animal(string).then(p => p.remove()); }; 

Next comes the basic error: the Pause function, which adds a continuation for

 var chain = Animal(...) 

But at the same time, it does not return the final Promise, but simply puts itself into the ready state, which is why the execution of the pause output is postponed to the next chain.

Instead, you need to return the Promise collected from the pause array using the reduce function.

 function Pause(pause) { return pause.reduce((chain, txt) => chain.then(() => AnimalPause(txt)), Promise.resolve()); }; 

In this case, the returned Promise will be embedded in the existing chain and called in the correct order.


Example assembly:

 var pause = [ '...', '.....', '....', '.........' ]; function Animal(string) { var a = ''; //variable which will be entered character by character string var i = 0; //Letter counter var p = document.createElement('p'); $('body').scrollTop($('body').append(p).height()); return new Promise(function(resolve) { Anima(); function Anima() { //Function of Animation a += string[i]; i++; p.textContent = a; var timer = setTimeout(Anima, 100); if (i == string.length) { clearTimeout(timer); resolve(p); }; }; }); }; function AnimalPause(string) { return Animal(string).then(p => p.remove()); }; function Pause(pause) { return pause.reduce((chain, txt) => chain.then(() => AnimalPause(txt)), Promise.resolve()); }; function Hello() { var chain = Animal('..........') .then(() => Animal('---Hello! CONSOLE v 1.0.1 is working!---')) .then(() => Animal('To see all commands u can use type -help')) .then(() => Pause(pause)) .then(() => Animal('DONE')) }; Hello(); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 

  • Thank you very much both for the bug fixed, and for clarifications and additions to improve the code! - mayst
  • I dealt with ES 6 only at the level of familiarization and .. Damn grateful !! - mayst