Reputation: 33
I've been trying loop through an array of items such that each is used in an operation that returns a promise, but within that promise there's another promise. I'm not getting the desired flow. What am I doing wrong? Or is there a better way. I couldn't use much chaining because of the conditionals. I have tried async/await in several ways still the same undesired result.
The codes have been simplified for clarity. The fetch
calls are actually database operations, but the behaviours are still the same; also I've used a single-element array in this case.
var names = ['mike'];
console.log('black');
var fn = function doThings(name) {
return new Promise((resolve) => {
console.log('blue');
var char = name.substr(1);
getNum(char);
console.log('violet');
function getNum(ch) {
console.log('green');
fetch('fetchfrom.url')
.then(response => {
console.log('orange');
return response.json();
})
.then(n => {
if(n === 2) {
console.log('red1');
fetch('fetchfrom.url')
.then(response => {
console.log('yellow');
return response.json();
}).then(color => {
if(n === 2) {
console.log('red2');
resolve(5);
}
else {
console.log('brown2');
resolve(10);
}
});
console.log('lilac');
} else {
console.log('brown1');
resolve(20);
}
});
}
})
}
var actions = names.map(fn);
Promise.all([actions])
.then(() => {
console.log('done');
})
I expect the logs to be in the order (assuming n always equals 2): black...blue...green...orange...red1...yellow...red2...lilac...violet...done
But instead i consistently get: black...blue...green...violet...done...orange...red1...yellow...red2...lilac
Upvotes: 2
Views: 98
Reputation: 3927
Another approach on plain promises, is using Promise.all
and playing an if-else
game with the results:
// These Promises are for ilustrative purposes, but indeed are your fetch's
const promiseA = Promise.resolve(2);
const promiseB = Promise.resolve(2);
// More than one 'name' in the array
const names = ['mike', 'john'];
console.log('black');
const getNum = ch => {
let response;
console.log('green');
return Promise.all([promiseA, promiseB]).then(([PromiseA, PromiseB]) => {
//^^^^^^^^^^^^^^^^^^^^^^ Resolve all promises to use them now with if-else
console.log('orange');
if (PromiseA == 2) {
console.log('red1');
console.log('yellow');
// In your example, you said: if(PromiseA==2). Here I use the second promise
if (PromiseB == 2) {
console.log('red2');
response = 5;
} else {
console.log('brown2');
response = 10;
}
console.log('lilac');
} else {
console.log('brown1');
response = 20;
}
return response;
});
};
const doThings = name => {
console.log(`=>>>>> Evaluating name ${name}`);
console.log('blue');
const char = name.substr(1);
return getNum(char).then(num => {
console.log('violet');
return num;
});
};
// Immediately resolving promise, and then chain new promises
// as the previous ones resolve
let op = Promise.resolve();
const actions = names.map(m => (op = op.then(() => doThings(m))));
Promise.all(actions)
.then(finalResponse => {
console.log(`done ${finalResponse}`);
})
.catch(e => console.log(`There was an error ${e}`));
This solution avoids using nested then-ables, and your code now looks more readable.
NOTE: The Promise.all
solution works semantically with concurrent execution of promises. If the semantical execution is secuentially, take a look here. But in all cases, semantical execution or not, the result will be the same.
Upvotes: 0
Reputation: 10710
You need to properly propagate the promises.
new Promise()
constructor is not needed in your example, you need to properly call .then()
whenever you do something async.fetch()
in a .then()
handler, return the promise to keep the promise-chain intact.fetch().then();
, because the code after that will be executed immediately instead of after the fetch call completed.You could use async / await
, in which your example would look something like this:
var names = ['mike'];
console.log('black');
async function getNum(ch) {
console.log('green');
let response = await fetch('fetchfrom.url');
console.log('orange');
let n = await response.json();
if (n === 2) {
console.log('red1');
let res = await fetch('fetchfrom.url');
console.log('yellow');
let color = await res.json();
if (n === 2) {
console.log('red2');
return 5;
} else {
console.log('brown2');
return 10;
}
console.log('lilac');
} else {
console.log('brown1');
return 20;
}
}
async function doThings(name) {
console.log('blue');
var char = name.substr(1);
let num = await getNum(char);
console.log('violet');
return num;
}
var actions = names.map(fn);
Promise.all([actions])
.then(() => {
console.log('done');
});
without async / await
& only plain promises you could do:
var names = ['mike'];
console.log('black');
function getNum(ch) {
console.log('green');
return fetch('fetchfrom.url').then(response => {
return response.json();
}).then(n => {
console.log('orange');
if (n === 2) {
console.log('red1');
return fetch('fetchfrom.url').then(res => {
console.log('yellow');
return res.json();
}).then(color => {
if (n === 2) {
console.log('red2');
return 5;
} else {
console.log('brown2');
return 10;
}
}).then(result => {
console.log('lilac');
return result;
});
} else {
console.log('brown1');
return 20;
}
});
}
function doThings(name) {
console.log('blue');
var char = name.substr(1);
return getNum(char).then(num => {
console.log('violet');
return num;
});
}
var actions = names.map(fn);
Promise.all([actions])
.then(() => {
console.log('done');
});
both should result in the expected log order.
Upvotes: 1