How to nested promise.all - javascript

I'm using es6 and have the following promises. What i want is the next Promise.all to wait for previous Promise.all to be completed before execute the next one. I have tried with the below codes but it's not working, only Promise 1 is resolved.
var deletePromises = [];
arr.menuItems.forEach((item, idx) => {
if (item.replace) {
deletePromises.push(deleteFromFirebase(user.uid, item));
}
});
// Promise 1
Promise.all(deletePromises).then(res1 => {
var uploadPromises = [], updateRecordPromises = [];
arr.menuItems.forEach((item, idx) => {
uploadPromises.push(uploadToFirebase(user.uid, item));
});
// Promise 2
Promise.all(uploadPromises).then(res2 => {
arr.menuItems.forEach((item, idx) => {
item.replace = false;
updateRecordPromises.push(updateRecord(user.uid, item));
});
// Promise 3
Promise.all(updateRecordPromises).then(res3 => {
console.log('All promise execute with successfully');
});
});
});
MarkM Answer
Try to use chaining as Mark suggest but the problem still there. I had found where the problem was, it is uploadPromises that never get resolved and then is never get called.
uploadToFirebase function
Stuck here, but the file is successfully uploaded. I can see all the files.
const uploadToFirebase = (userid, item) => {
return new Promise((resolve, reject) => {
const uploadUri = Platform.OS === "ios"
? RNFetchBlob.wrap(item.pic_url.replace("file://", ""))
: RNFetchBlob.wrap(item.pic_url);
Blob.build(uploadUri, {
type: "image/png;"
}).then(blob => {
// upload image using Firebase SDK
firebase
.storage()
.ref("menu_items")
.child(userid)
.child(item.new_filename)
.put(blob, { contentType: "image/png" })
.then(snapshot => {
console.log("Promise resolve: ", snapshot);
resolve(snapshot);
blob.close();
})
.catch(error => {
reject(error.message);
});
});
});
};
Updated code
console.log('Print res2') is not printed
var deletePromises = [],
uploadPromises = [],
updateRecordPromises = [];
arr.menuItems.forEach((item, idx) => {
if (item.replace) {
deletePromises.push(deleteFromFirebase(user.uid, item));
}
});
Promise.all(deletePromises)
.then(res1 => {
console.log("Print res1:", res1);
arr.menuItems.forEach((item, idx) => {
uploadPromises.push(uploadToFirebase(user.uid, item));
});
return Promise.all(uploadPromises);
})
.then(res2 => {
console.log("Print res2:", res2);
dispatch({ type: MENU_UPDATE_SUCCESS, payload: arr });
dispatch(reset("menuItem"));
})
.catch(error => {
console.log("Print error:", error);
});

You don't need to nest the Promises, you can return your new Promise.all from then(), which will let you chain them. Easier with an example:
var arr = [1, 2, 3, 4, 5, 6]
function slowF(i) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(i), i*200)
})
}
var p = arr.map((i) => slowF(i))
Promise.all(p)
.then((p) => {
console.log("In first Promise.all resolving: ", p)
var newP = p.map(i => slowF(i) )
return Promise.all(newP)
})
.then((p)=>{
console.log("In second Promise.all resolving: ", p)
})
.catch((e) => console.log("error: ", e))

I found the solution. It is because one of menuItems array prop -> new_filename is empty, thats why promise never get resolved. So, i just need to add empty checking for each of item prop and then the promise was resolved properly. Thanks to #MarkM for the answer. Now the code much more cleaner and easier to read compare to the nested promise.
Promise.all(deletePromises)
.then(res1 => {
console.log("Print res1:", res1);
arr.menuItems.forEach((item, idx) => {
if (!isEmpty(item.new_filename)) {
uploadPromises.push(uploadToFirebase(user.uid, item));
}
});
return Promise.all(uploadPromises);
})
.then(res2 => {
console.log("Print res2:", res2);
dispatch({ type: MENU_UPDATE_SUCCESS, payload: arr });
dispatch(reset("menuItem"));
})
.catch(error => {
console.log("Print error:", error);
});

Related

How to make two api calls using Promise.all within Angular9?

I making an api call using Promise.all as below:
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
});
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
The above code is working fine.
Now I want to make another api call after the successful completion of this.serviceB.retry(oretry).
The second api is this.serviceB.createDbEntry(sentry) and sentry looks as below:
const sretry: SDInterface = {
hostName,
Id: this.Id.slice(0, this.Id.length),
reason: this.reason
};
And, I am doing it as below
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
const sretry: SDInterface = {
hostName,
Id: this.Id.slice(0, this.Id.length),
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
this.serviceB.createDbEntry(sentry).subscribe(resolve);
});
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
The above code is giving an error:
error: "SequelizeValidationError: string violation: Id cannot be an array or an object"
It is looks like it is not calling the second api for every Id
You may want to take a look a forkJoin
import { Observable, forkJoin } from 'rxjs';
And then
ngOnInit() {
let one = this.http.get('some/api/1') //some observable;
let two = this.http.get('some/api/2') // another observable;
forkJoin([one, tow]).subscribe(response => {
// results[0] is our one call
// results[1] is our second call
let var1 = response[1];
let var2 = response[0];
}/*, error => { in case error handler } */);
}
Wouldn't it be better to use Promise.all() once more?
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
});
})
.then(() => {
return Promise.all(this.Id.slice(0, this.Id.length).map(id => {
return new Promise((resolve, reject) => {
const sretry: SDInterface = {
hostName,
Id: id,
reason: this.reason
};
this.serviceB.createDbEntry(sentry).subscribe(resolve);
});
})
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
And using toPromise() will make the code more concise.
Promise.all(this.hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
return this.serviceB.retry(oretry).toPromise();
})
.then(() => {
return Promise.all(this.Id.slice(0, this.Id.length).map(id => {
const sretry: SDInterface = {
hostName,
Id: id,
reason: this.reason
};
this.serviceB.createDbEntry(sentry).toPromise();
})
});
}))
.then(() => {
this.dialog.close();
})
.catch(err => {
console.log(err);
});
Use combineLatest, in Angular we use RxJs not promises.
combineLatest(
[this.http.get('call1'), this.http.get('call2')]
).subscribe(([result1, result2]) => {
// do stuff with result1 and result2
});
promise.all takes input in an array and gives response in an array,
Create 2 functions each with your asynchronous logic returning a promise,
Say funcA and funcB, then use below to invoke them parellely
Promise.all([funcA(this.hostName), funcB(this.id)])
.then(respones => {
console.log(responses[0]); //return value for funcA
console.log(responses[1]); //return value for funcB
})
.catch(err => console.log(err));
I am assuming your logic of functions are correct, I just copy-pasted from your question and gave them structure
const funcA = (hostName) => {
hostName.slice(0, this.Id.length).map((hostName) => {
return this.serviceC.status(hostName)
.then(res => {
return new Promise((resolve, reject) => {
const oretry: ORInterface = {
oQid: res.rows[0].qid,
reason: this.reason
};
this.serviceB.retry(oretry).subscribe(resolve);
});
});
});
}
const funcB = (Id) => {
Id.slice(0, this.Id.length).map(id => {
return new Promise((resolve, reject) => {
const sretry: SDInterface = {
hostName,
Id: id,
reason: this.reason
};
this.serviceB.createDbEntry(sentry).subscribe(resolve);
});
})
}

Node.js Promise with mongoose

I have difficulty using Promise.
I want to get data from one more tables in mongodb.
but I fell in callback hell, So I tried to solve this but I couldn't.
What should I do? the result came out 'undefined'.
Many Thanks,
let mongoose = require('mongoose');
mongoose.Promise = global.Promise;
....
exports.Recommend = (id) => {
User.find({User_id: myId})
.then((result) => {
return Promise.resolve(result[0].age)
}).then(age => {
return new Promise((resolve,rejject)=>{
resolve(User.find()
.select('User_id')
.where('age').equals(age))
})
}).then((Users_id) => {
Users_id.forEach((user, idx, arr) => {
Count.find()
.select('board_id')
.where('User_id').equals(user.User_id)
.exec((err, items) => {
return new Promise((resolve,reject)=>{
resolve(
items.forEach((post, idx, arr) => {
posts.push(post.board_id)
}))
})
})
})
}).then(()=>{
console.log("posts:"+posts);
})
}
Avoid Promise.resolve, avoid using the new Promise constructor like Promise.resolve, avoid the Promise constructor antipattern, and avoid forEach, and don't forget to return the promise chain from your function:
exports.Recommend = (id) => {
return User.find({User_id: myId}).then(result => {
return User.find()
.select('User_id')
.where('age')
.equals(result[0].age));
}).then(user_ids => {
return Promise.all(user_ids.map((user, idx, arr) => {
return Count.find()
.select('board_id')
.where('User_id').equals(user.User_id)
.exec()
.then(posts => posts.map(post => post.board_id));
}));
}).then(board_ids => {
console.log("posts:"+board_ids);
})
}
You have the problem with 3rd .then, I would like to recommend you to use Promise.all function to run the parallel database query. Following example may help you
exports.Recommend = (id) => {
User.find({
User_id: myId
})
.then((result) => {
return User.find()
.select('User_id')
.where('age').equals(result[0].age)
}).then((Users_id) => {
return Promise.all(Users_id.map((user, idx, arr) => {
return Count.find()
.select('board_id')
.where('User_id').equals(user.User_id)
}));
}).then((Users_id) => {
Users_id.forEach(items => {
items.forEach(post => {
posts.push(post.board_id)
})
})
}).then(() => {
console.log("posts:" + posts);
})
}

async issues with js generator and promises not returning result

I'm having yet another async issue where I'm lost and have no idea where or how to fix it. Forgive my bad naming.
api call to twitch api and returns an array its results.
exports.batchPromiseWrapper = function(arr) {
const filteredMungedDataArr = [];
let promiseBatachArray = arr.map(vod_id => {
var url = `https://api.twitch.tv/kraken/videos/${vod_id.id}/markers`;
var params = { api_version: 5 };
return axios
.get(url, {
params: params,
headers: {
"Client-ID": "xxxxxxxxxxxxxxx"
}
})
.then(res => {
return res.data;
})
.catch(function(error) {
console.log(error);
});
});
return Promise.all(promiseBatachArray)
.then(markers => {
if (markers !== null) {
markers.map(markerObj => {
if (markerObj.markers.game_changes !== null) {
markerObj.markers.game_changes.forEach(gameName => {
if (gameName.label === "Fortnite") {
filteredMungedDataArr.push(markerObj);
}
});
}
});
return filteredMungedDataArr;
}
})
.catch(err => {
if (err.status === 500 || err.status === 404) {
console.log("error: ", err, err.message);
}
});
};
The data looks like this:
[[1,2,3,4,5],[1,2,3,4,5]], generator will yield and make a promise.all call of 5 before pausing 5sec and continuing to the next batch of 5.
exports.batchFetchingGeneratorWrapper = function(generator, batchArray) {
let evalNextValue = generator.next();
let delay = (v, t) => {
return new Promise(resolve => {
setTimeout(resolve.bind(null, v), t);
});
};
if (!evalNextValue.done) {
exports.batchPromiseWrapper(evalNextValue.value).then(data => {
let newBatchArray = batchArray;
if (data !== undefined) {
newBatchArray = batchArray.concat(data);
}
delay(5000).then(() => {
exports.batchFetchingGeneratorWrapper(generator, newBatchArray);
});
});
} else {
console.log("yay done!", batchArray);
return batchArray;
}
};
I'm able to console the results in batchArray from batchFetchingGeneratorWrapper, but I unable to act on it and I know it has something to do with async and how it has yet to be resolved.
promiseDataWrapper
.then(data => {
return gatherData.cleanUpVODData(data);
})
.then(data => {
function* batchFetching(batchArray) {
for (let i = 0; i < batchArray.length; i++) {
yield batchArray[i];
}
}
let batchArrResult = [];
let g = batchFetching(data);
new Promise((resolve, reject) => {
gatherData.batchFetchingGeneratorWrapper(g, batchArrResult);
if (g.done) { // i dont think this works
console.log("batchArrResult 1: ", batchArrResult);
resolve(batchArrResult);
}
}).then(result => console.log("asdfasdf", batchArrResult)); // empty array is returned
});
As far as I can tell, the problem lies chiefly in batchFetchingGeneratorWrapper().
It should be a matter of :
fixing delay()
making appropriate returns to make the recursion work
ensuring that the function returns Promise.
Almost undoubtedly (syntactically) simpler with async/await but here it is with old-fashioned thens :
exports.batchFetchingGeneratorWrapper = function(generator, batchArray) {
let evalNextValue = generator.next();
let delay = (t) => {
return new Promise(resolve => {
setTimeout(resolve, t);
});
};
if (!evalNextValue.done) {
return exports.batchPromiseWrapper(evalNextValue.value).then(data => {
return delay(5000).then(() => {
return exports.batchFetchingGeneratorWrapper(generator, batchArray.concat(data || []));
});
});
} else {
console.log("yay done!", batchArray);
return Promise.resolve(batchArray); // <<< promise wrapped to ensure that batchFetchingGeneratorWrapper() returns Promise
}
};
And chain the batchFetchingGeneratorWrapper() call appropriately :
promiseDataWrapper
.then(data => gatherData.cleanUpVODData(data))
.then(data => {
function* batchFetching(batchArray) {
for (let i = 0; i < batchArray.length; i++) {
yield batchArray[i];
}
}
return gatherData.batchFetchingGeneratorWrapper(batchFetching(data), []).then(batchArrResult => {
console.log('batchArrResult: ', batchArrResult);
return batchArrResult;
});
}).catch(error => {
console.log(error);
});

Do forEach async requests inside of a promise?

Need help related to promises. Please refer to details below, topic is more theoretical, I don't understand what flow should I use:
We have async function getDataFromUri(), which returns data, which gets filtered and saved to arr of objects, lets name it res;
For each campaign(object) inside of array res I want send async request which will store products images of campaign to object. As result I should have ONE array res where ALL filtered data (campaign name, campaign images) is stored;
I need smth like:
[{
name: "Somename",
images: ['uri', 'uri', 'uri', 'uri', 'uri', 'uri']
},{
name: "Somename",
images: ['uri', 'uri', 'uri', 'uri', 'uri', 'uri']
}
]
Do some other actions with RES array.
This function gets campaigns:
function getDataFromUri(uri) {
return new Promise((resolve, reject) => {
request.get(uri, (err, res, body) => {
if(err || res.statusCode !== 200 ) {
reject(handleErr(err));
} else {
resolve(body);
}
});
});
}
This function gets images of campaign:
function getProductsOfCampaign(id) {
var productsImagesLinks = [];
return new Promise((resolve, reject) => {
getDataFromUri(`SOME_URI/${id}.json`)
.then((json) => {
var productsList = JSON.parse(json).products;
resolve (productsList.map((product) => product.imgSrc));
}).catch((e) => {
throw new Error(e);
})
});
}
Here I met problem:
getDataFromUri(someLink) //Get campaings;
.then((result) => {
//NOT WORKING FOREACH
result.forEach((item, i) => {
item.images = getProductsOfCampaign(item.id);
})
return result;
})
.then((result) => {
//Do something else to array with images;
});
How can I force next after forEach .then() expression to wait all images URLs to be saved?
I tried Promise.all(), but seems have lack of knowledge on how to implement it correct way.
I will really appreciate if you help me resolve this case. Thank you.
Observe that:
item in forEach is a copy.
getProductsOfCampaign returns a Promise.
The web is a best-effort service.
Do this:
getDataFromUri(someLink) // Get campaigns
.then(result => {
var promises = result.map(item =>
getProductsOfCampaign(item.id)
.then(products => {
item.images = products;
return item;
})
// 3: Best-effort service
.catch(() => {})
);
return Promise.all(promises);
}).then(items => {
console.log(items);
// Do something else to array of items with images
});
Other readers can test for correctness with this:
function getDataFromUri(someLink) {
return new Promise((resolve) => {
setTimeout(resolve, 1000, [{id: 1}, {id: 2}]);
})
}
function getProductsOfCampaign(id) {
return new Promise((resolve) => {
setTimeout(resolve, 1000, id * id);
})
}
var someLink = '';
Thanks to Benjamin Gruenbaum for suggesting that .catch(() => {}) can be used with Promise.all for a best-effort service.
let campaigns = null;
getDataFromUri(someLink) //Get campaings;
.then((result) => {
campaigns = result;
let pImages = []
result.forEach((item, i) => {
pImages.push(getProductsOfCampaign(item.id));
});
return Promise.all(pImages);
})
.then((images) => {
campaigns.forEach((campaign, index) => {
campaign.images = images[index];
});
// ... Do something else to array with images;
});

Javascript Map that waits for previous promise before starting next?

I know this isn't in the scope of a Array.map but I'd like to wait until the previous item has finished its promise before starting the next one. It just happens that I need to wait for the previous entry to be saved in the db before moving forwards.
const statsPromise = stats.map((item) => {
return playersApi.getOrAddPlayer(item, clubInfo, year); //I need these to wait until previous has finished its promise.
});
Promise.all(statsPromise)
.then((teamData) => {
..//
});
playersApi.getOrAddPlayer returns a new Promise
Edit
Reading more on it, it seems its important to show playersApi.getOrAddPlayer
getOrAddPlayer: function (item, clubInfo, year) {
return new Promise((resolve, reject) => {
var playerName = item.name.split(' '),
fname = playerName[0].caps(),
sname = playerName[1].caps();
Players.find({
fname: fname,
sname: sname,
}).exec()
.then(function(playerDetails, err){
if(err) reject(err);
var savePlayer = new Players();
//stuff
savePlayer.save()
.then(function(data, err){
if(err) reject(err);
item._id = data._id;
resolve(item);
});
});
});
}
You can use reduction instead of mapping to achieve this:
stats.reduce(
(chain, item) =>
// append the promise creating function to the chain
chain.then(() => playersApi.getOrAddPlayer(item, clubInfo, year)),
// start the promise chain from a resolved promise
Promise.resolve()
).then(() =>
// all finished, one after the other
);
Demonstration:
const timeoutPromise = x => {
console.log(`starting ${x}`);
return new Promise(resolve => setTimeout(() => {
console.log(`resolving ${x}`);
resolve(x);
}, Math.random() * 2000));
};
[1, 2, 3].reduce(
(chain, item) => chain.then(() => timeoutPromise(item)),
Promise.resolve()
).then(() =>
console.log('all finished, one after the other')
);
If you need to accumulate the values, you can propagate the result through the reduction:
stats
.reduce(
(chain, item) =>
// append the promise creating function to the chain
chain.then(results =>
playersApi.getOrAddPlayer(item, clubInfo, year).then(data =>
// concat each result from the api call into an array
results.concat(data)
)
),
// start the promise chain from a resolved promise and results array
Promise.resolve([])
)
.then(results => {
// all finished, one after the other
// results array contains the resolved value from each promise
});
Demonstration:
const timeoutPromise = x => {
console.log(`starting ${x}`);
return new Promise(resolve =>
setTimeout(() => {
console.log(`resolving result for ${x}`);
resolve(`result for ${x}`);
}, Math.random() * 2000)
);
};
function getStuffInOrder(initialStuff) {
return initialStuff
.reduce(
(chain, item) =>
chain.then(results =>
timeoutPromise(item).then(data => results.concat(data))
),
Promise.resolve([])
)
}
getStuffInOrder([1, 2, 3]).then(console.log);
Variation #1: Array.prototype.concat looks more elegant but will create a new array on each concatenation. For efficiency purpose, you can use Array.prototype.push with a bit more boilerplate:
stats
.reduce(
(chain, item) =>
chain.then(results =>
playersApi.getOrAddPlayer(item, clubInfo, year).then(data => {
// push each result from the api call into an array and return the array
results.push(data);
return results;
})
),
Promise.resolve([])
)
.then(results => {
});
Demonstration:
const timeoutPromise = x => {
console.log(`starting ${x}`);
return new Promise(resolve =>
setTimeout(() => {
console.log(`resolving result for ${x}`);
resolve(`result for ${x}`);
}, Math.random() * 2000)
);
};
function getStuffInOrder(initialStuff) {
return initialStuff
.reduce(
(chain, item) =>
chain.then(results =>
timeoutPromise(item).then(data => {
results.push(data);
return results;
})
),
Promise.resolve([])
);
}
getStuffInOrder([1, 2, 3]).then(console.log);
Variation #2: You can lift the results variable to the upper scope. This would remove the need to nest the functions to make results available via the nearest closure when accumulating data and instead make it globally available to the whole chain.
const results = [];
stats
.reduce(
(chain, item) =>
chain
.then(() => playersApi.getOrAddPlayer(item, clubInfo, year))
.then(data => {
// push each result from the api call into the globally available results array
results.push(data);
}),
Promise.resolve()
)
.then(() => {
// use results here
});
Demonstration:
const timeoutPromise = x => {
console.log(`starting ${x}`);
return new Promise(resolve =>
setTimeout(() => {
console.log(`resolving result for ${x}`);
resolve(`result for ${x}`);
}, Math.random() * 2000)
);
};
function getStuffInOrder(initialStuff) {
const results = [];
return initialStuff.reduce(
(chain, item) =>
chain
.then(() => timeoutPromise(item))
.then(data => {
results.push(data);
return results;
}),
Promise.resolve()
);
}
getStuffInOrder([1, 2, 3]).then(console.log);
If you are fine with using promise library, you can use Promise.mapSeries by Bluebird for this case.
Example:
const Promise = require("bluebird");
//iterate over the array serially, in-order
Promise.mapSeries(stats, (item) => {
return playersApi.getOrAddPlayer(item, clubInfo, year));
}).then((teamData) => {
..//
});
You can use a recursion solution
const statsPromise = (function s(p, results) {
return p.length ? playersApi.getOrAddPlayer(p.shift(), clubInfo, year) : results;
})(stats.slice(0), []);
statsPromise
.then((teamData) => {
//do stuff
});
let n = 0;
let promise = () => new Promise(resolve =>
setTimeout(resolve.bind(null, n++), 1000 * 1 + Math.random()));
let stats = [promise, promise, promise];
const statsPromise = (function s(p, results) {
return p.length ? p.shift().call().then(result => {
console.log(result);
return s(p, [...results, result])
}) : results;
})(stats.slice(0), []);
statsPromise.then(res => console.log(res))
You could use a kind of recursion:
function doStats([head, ...tail]) {
return !head ? Promise.resolve() :
playersApi.getOrAddPlayer(head, clubInfo, year)
.then(() => doStats(tail));
}
doStats(stats)
.then(() => console.log("all done"), e => console.log("something failed", e));
Another classic approach is to use reduce:
function doStats(items) {
return items.reduce(
(promise, item) =>
promise.then(() => playersApi.getOrAddPlayer(item, clubInfo, year)),
Promise.resolve());
By the way, you could clean up your getOrAddPlayer function quite a bit, and avoid the promise constructor anti-pattern, with:
getOrAddPlayer: function (item, clubInfo, year) {
var playerName = item.name.split(' '),
fname = playerName[0].caps(),
sname = playerName[1].caps();
return Players.find({fname, sname}).exec()
.then(playerDetails => new Players().save())
.then({_id} => Object.assign(item, {_id}));
}
I gave it a thought but I didn't find a better method than the reduce one.
Adapted to your case it would be something like this:
const players = [];
const lastPromise = stats.reduce((promise, item) => {
return promise.then(playerInfo => {
// first iteration will be undefined
if (playerInfo) {
players.push(playerInfo)
}
return playersApi.getOrAddPlayer(item, clubInfo, year);
});
}, Promise.resolve());
// assigned last promise to a variable in order to make it easier to understand
lastPromise.then(lastPlayer => players.push(lastPlayer));
You can see some explanation about this here.

Categories