4

How can I make my code run sequentially? For example,

  1. If I have a for loop which gets some data from a service, I want the n+1 iteration to run only after the nth iteration has completed.

  2. I want the code after the loop to execute only after the for loop has completed all interactions.

Example code:

someMethod() {

    for ( var i = 0; i < someLength; i++) {
        // get some data
        this.dataService.get(i).subscribe(data => {
            // do something with the data
        }); 
    }

    // 
    console.log ('print me only after all iterations');

    // ....
    // some more lines of code
}

Here is another example ( Plunker ) :

someMethod() {

    for ( var i = 0; i < 5; i++) {
        setTimeout(()=> {
            console.log('This is iteration' + i); 
        },500);
    }

    // I want to execute this line of code only after the 
    // for loop has completed all iterations. 
    console.log ('print me only after all iterations');

    // ....
    // some more lines of code
}

Any ideas?

Faisal
  • 27,816
  • 7
  • 82
  • 93

8 Answers8

8

You could wrap each iteration in a Promise and await it:

async function someMethod() {
    for (var i = 0; i < 5; i++) {
        await new Promise(resolve => {
            setTimeout(()=> {
                console.log('This is iteration ' + i); 
                resolve();
            }, 500);
        });
    }
    console.log ('print me only after all iterations');
}
someMethod();
Cristi Mihai
  • 2,275
  • 1
  • 18
  • 20
  • 1
    Plus 1 Nice answer. It might be worth mentioning `toPromise` – Aluan Haddad Aug 12 '17 at 11:30
  • This does not work. `console.log('print me only after all iterations');` is printed before the for loop has completed its iterations and also the iterations are not in order. – Faisal Aug 14 '17 at 10:43
  • 1
    I've updated the example to be only console logs. The async and await are important. You can test this code at Typescript playground or even run it as is in Chrome (https://jsfiddle.net/xzrjds5n/). The `async` / `await` keywords are important. – Cristi Mihai Aug 15 '17 at 21:08
1

Are you looking for something like this? (plunker)

export class App {
  name:string;
  constructor() {
    this.name = `Angular! v${VERSION.full}`;

    this.someMethod();
  }

  doTimeout(currentIndex:int){
    return new Promise(resolve => {
      setTimeout(()=> {
        console.log("This is iteration " + currentIndex);
        resolve();
      },500);
    });
  }

  async someMethod() {
    for(let i=0;i<5;i++){
      await this.doTimeout(i);
    }
    // I want to execute this line of code only after the 
    // for loop has completed all iterations. 
    console.log ('print me only after all iterations'); 

    // ....
    // some more lines of code
  }
}

Sources: What is the JavaScipt Version of sleep? and Combination of async function + await + setTimeout

Michael
  • 338
  • 3
  • 8
  • No I am not exactly looking for this. In any other programming language, C# for example, the code that comes after the loop will only execute once all the iterations have completed. I am looking for the same here. To run this line `console.log('print me only after all iterations');`, only after the for-loop has completely iterated – Faisal Aug 14 '17 at 13:31
  • 1
    I think I see what you're saying.. how about [this?](https://plnkr.co/edit/wqiReHjBeGyf8D4q134H) – Michael Aug 14 '17 at 14:34
  • yes, something like that. I will test that further with more examples. – Faisal Aug 14 '17 at 14:57
  • 1
    @Faisal if this helped guide you to your solution, could you mark it as the answer? – Michael Aug 16 '17 at 13:13
1

As Javascript is a single thread language, it is hard to do what exactly you want to achieve unless you use Promise or other callback architecture.

There are many ways to achieve it through PROMISEs. Here, I'll show you how can you achieve the same using advance JavaScript concept called Async & Await in Angular framework.

Please note there are many ways with Promise but my intention is to introduce you to new async & await concept where you don't need to chain the callback for success and reject methods.

DEMO : https://plnkr.co/edit/16k66yRjXLPwTM50kYNS

export class App {
  name:string;
  constructor() {
    this.name = `Angular! v${VERSION.full}`;
    this.someMethod(); 
  }

 const someMethod_Promise = () => {
  return new Promise((resolve,reject)=>{
     for ( var i = 0; i < 5; i++) {
       console.log('printing value of i ' + i);
        if(i==4)   // real condition
         resolve(true);
     } 
  })
 }

 const someMethod = async()=>{
   let result= await this.someMethod_Promise();
   if(result){
      console.log ('print me only after all iterations'); 
   }
 }
}
micronyks
  • 49,594
  • 15
  • 97
  • 129
1

Promise based solution:

Promise.all() takes an array of promises and fires them all at the same time. Then once all of the promises resolve it will run the callback.

let dataService = {
  get: function(i) {
    return new Promise((resolve, reject) => {
      setTimeout(resolve, 100, i);
    });
  }
}

let promises = [];

for (var i = 0; i < 3; i++) {
    // get some data
    promises.push(dataService.get(i));
}

Promise.all(promises).then(values => { 
  console.log ('print me only after all iterations');
  console.log(values); 
});

Working sample: http://jsbin.com/ruqiwoxono/edit?js,console

Promise.all docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

Rxjs based solution:

forkJoin basically does the same thing as Promise.all() but for observables

let dataService = {
  get: function(i) {
    return Rx.Observable.of(i);
  }
}

let observables = [];

for (var i = 0; i < 3; i++) {
    // get some data
    observables.push(dataService.get(i));
}


const example = Rx.Observable.forkJoin(observables);


const subscribe = example.subscribe(values => {
  console.log ('print me only after all iterations');
  console.log(values); 
});

Working sample: http://jsbin.com/jolotuyoxa/edit?js,console

Rxjs forkjoin docs: https://www.learnrxjs.io/operators/combination/forkjoin.html

seescode
  • 1,938
  • 12
  • 31
0

I suggest adapting it to use the zip "combining operator" from rxjs:

http://reactivex.io/documentation/operators/zip.html

EDIT: here is an example: https://www.learnrxjs.io/operators/combination/zip.html

Although in your case you would have to pass it as an array probably ( zipArray).

KarolDepka
  • 7,188
  • 9
  • 37
  • 55
0

You can do it with Promise.

someMethod() : Promise<any> {

 return new Promise((resolve) => {
  for ( var i = 0; i < someLength; i++) {
        // get some data
        this.dataService.get(i).subscribe(data => {
            // do something with the data
        }); 

if(i==someLength-1){resolve();}
    }


}).then(()=>{ 


    // 
    console.log ('print me only after all iterations');

    // ....
    // some more lines of code
})
}
Sarat Chandra
  • 4,192
  • 23
  • 27
-1

I think you need exact like this..
Just Run code snippets and see the result

async function someMethod() {
    for (var i = 0; i < 5; i++) {
        await new Promise(resolve => {
            setTimeout(()=> {
                console.log('This is iteration ' + i); 
                resolve();
            }, 1500);
        });
    }
    console.log ('print me only after all iterations');
}
someMethod();
Rohit Tagadiya
  • 1,473
  • 1
  • 8
  • 16
-2
SomeMethod(i, length){
       If(i< length){
          this.dataService.get(i).subscribe(  data => {
                 // do some business logic 
                 this.SomeMethod(i+1, length);

            }); 
       }else{
           console.log("Iteration completed");
       }}

Try this logical way

Raja Mohamed
  • 966
  • 8
  • 19