How to use Promise.all and ForEach to make ajax call per array item?
-
07-02-2021 - |
Question
I have 2 arrays with email address that I want to feed to the function below, one after the other (not in parallel) and then perform a final function.
I've been looking at examples trying to figure out the correct way to chain these, but I think I'm now more confused than when I started. Tried playing with Promise.all to ensure steps completed before going to next but that was a disaster.
eg: (do array 1).then(do array 2).then(final function)
EmailArray.forEach(fGetUserId(X)) and push to IdArray
function fGetUserId(X) {
$.ajax({ // get the Ids of users, return to populate IdArray
url: xhost + "/portal/_api/web/SiteUsers?$select=Id&$filter=Email eq '" + X + "'",
type: "GET", headers: { "Accept": "application/json;odata=verbose", }, //verbose
success: function (data) {
var xuserid = data.d.results[0].Id;
return xuserid;
},
error: function (error) { alert(JSON.stringify(error)); }
});
}
Help?
Solution
JQuery AJAX does not use native JS promises. You can observe this by the way they defined a success and error callback.
You can explore using fetch
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API, which is a reasonable browser API since you are already using Promise
. I'll provide examples for both fetch
and ajax
:
/**
@returns {Promise.<number|void>}
*/
async function fGetUserId(x) {
/** @type {Promise.<any>} */
let fetchPromise = fetch(xhost + "/portal/_api/web/SiteUsers?$select=Id&$filter=Email eq '" + X + "'", {
method: "GET",
headers: {
Accept: "application/json;odata=verbose"
}
}
).then(x => x.json()); // convert response to JSON, aka Promise.<Response> to Promise.<any>
let idPromise = fetchPromise
.then(data => {
var xuserid = data.d.results[0].Id;
return xuserid;
}) // Promise.<any> to Promise.<number>
.catch(err => alert(JSON.stringify(err))); // in case of error, it becomes Promise.<void> instead, since we do not return anything within the catch()
let userid = await idPromise; // Promise.<number|void> to number|void.
return userid; // number (but actually Promise.<number|void> since fGetUserId has the async modifier in its signature)
}
/**
@returns {Promise.<number|void>}
*/
async function fGetUserId(x) {
/** @type {Promise.<number|void>} */
let myPromise = new Promise((resolve, reject) => {
$.ajax({
url: xhost + "/portal/_api/web/SiteUsers?$select=Id&$filter=Email eq '" + X + "'",
type: "GET",
headers: { Accept: "application/json;odata=verbose", }, //verbose
success: function (data) {
var xuserid = data.d.results[0].Id;
resolve(xuserid); // signify the promise succeeded with xuserid as return value
},
error: function (error) {
alert(JSON.stringify(error));
reject(); // signify the promise faulted with no return value
}
});
});
let userid = await myPromise; // Promise.<number|void> to number|void.
return userid; // number (but actually Promise.<number|void> since fGetUserId has the async modifier in its signature)
}
There might be slight errors as I didn't test the code, but I hope the gist is still conveyed!
To call this code, you run this:
/** @type {string[]} */
let EmailArr = ["..", "..", ".."];
/** @type{(Promise.<number|void>)[]} */
let promiseArr = EmailArr.map(x => fGetUserId(x)); // map an array of emails to an array of promises
Promise.all(promiseArr) // wait for array of promises to resolve into array of userid, (aka Promise.<number|void>)[] to (number|void)[]
.then(userids => console.log("done!", userids)
.catch(err => console.error(err));
Note that Promise.all() itself is a Promise. You can use .then()
to handle it as I did, or use await
. Note that await
can only be used in an async function, so you might want to declare a global anonymous async function:
(async () => {
// You can use "await" here
})();