5

I'm trying to create a form that allows you to create multiple resources in sequential order.

Example below

Floor 1
Floor 2
Floor 3
...
Floor 9

The problem with the code is that the order is not guarantee.

My code below

let startAt = this.addAreasForm.controls['startAt'].value
const name = this.addAreasForm.controls['name'].value
const newArea = {name: name}

for (let i = 1; i < (amount + 1); i++) {
  newArea.name = name + ' ' + startAt
  startAt++
  this.areasService.createArea(newArea, parentId)
    .subscribe(
      area => this.added.emit(area)
    )
}

Can come back like

Floor 2
Floor 3
Floor 1
Floor 5
Floor 4

How do you handle async api calls to guarantee sequential order?

2
  • Maybe you can concat observables inside for loop, and subscribe to a result after the loop is finished? reactivex.io/rxjs/class/es6/… Commented Aug 17, 2017 at 14:05
  • You know, when you update the name property of the newArea object all previously set values are updated right..? Objects are reference type. Other than that i would recommend inserting promises sequentially in an array and reducing them by .then() stages. Commented Aug 17, 2017 at 14:24

2 Answers 2

5

You can use async / await for that purpose with the Promise resolve:

for (let i = 1; i < (amount + 1); i++) {
    await new Promise(resolve => {
        newArea.name = name + ' ' + startAt
        startAt++
        this.areasService.createArea(newArea, parentId)
            .subscribe(
                area => { 
                    this.added.emit(area);
                    resolve();
                });
        });
}

Remember to put async before your function. See this demo on StackBlitz.

Sign up to request clarification or add additional context in comments.

5 Comments

My http calls are using observables. Will this still apply?
yes, that will work with Observables as well. It will wait for one observable to complete, and then resolve the promise and execute the next iteration.
A live example would be cool, plunker is blocked by my company.
@Omzig I have updated the demo to StackBlitz
This is so much easier than Promise.all() and you do not lose scope from your front end. You do not have to copy your services into local var's.
1

You can try something like this, I don't exactly all your code from your services, but the main idea is this: In order to execute async code in order, you can build an array of promises and then to use Promise.all to take each result in the same order from the creation: Promise.all

let startAt = this.addAreasForm.controls['startAt'].value;
const name = this.addAreasForm.controls['name'].value;
const newArea = {name: name};

Keep your services into variables I don't know from where your context comes.

 const areasService = this.areasService,
    added = this.added;

Make a function that create a promise for your subscribe:

function createAreaPromise(newArea, parentId) {
    return new Promise((resolve, reject) => {
        areasService.createArea(newArea, parentId)
            .subscribe(area => resolve(area));
    });
}

Than another function to build multiple an array of promises:

function buildPromises() {
    let promises = [];

    for (let i = 1; i < (amount + 1); i++) {
      newArea.name = name + ' ' + startAt
      startAt++
      promises.push(createAreaPromise(newArea, parentId));
    }

    return promises;
}

Then solve them with Promise.all, to obtain the same order from creation

let promises = buildPromises();
Promise.all(promises)
    .then(results => {
        results.forEach(result => added.emit(result));
    });

Here a live example:

function random() {
	return Math.floor(Math.random() * 5);
}

function makePromise(index) {
	return new Promise((resolve) => {
		setTimeout(() => {
			resolve(index);
		}, random() * 1000);
	});
}

function buildPromises() {
	let promises = [];
	for(let i = 0; i < 5; i++) {
		promises.push(makePromise(i));
	}

	return promises;
}

let promises = buildPromises();
Promise.all(promises)
	.then(results => {
		results.forEach(result => {
			console.log(result);
		});
	});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.