I'm trying to write a function but can't seem to figure out how to run a loop of API calls on an array of strings, and only after all the API calls are completed to call the callback function. No matter what I try either the function fails or the callback function is called too soon.
Here is my code:
exports.handler = function (context, event, callback) {
const client = context.getTwilioClient();
const sidArr = [];
const cc = event.ccSid;
const exp = event.expSid;
const cvc = event.cvcSid;
sidArr.push(cc, exp, cvc);
const resArr = [];
for (var i = 0; i < sidArr.length; i ) {
client
.messages(sidArr[i])
.update({ body: '' })
.then((message) => {
if (resArray.length == sidArr.length) {
callback(null, resArray);
} else {
resArray.push(message.sid);
}
});
}
};
CodePudding user response:
If you can't use async
and await
, then you can just use Promise.all()
and run them in parallel like this:
Run them in parallel:
let promises = [];
for (var i = 0; i < sidArr.length; i ) {
promises.push(
client
.messages(sidArr[i])
.update({ body: '' })
.then((message) => message.sid)
);
}
Promise.all(promises)
.then((results) => callback(null, results))
.catch((err) => callback(err));
Of course, it's a bit of an anti-pattern to be turning a promise into a plain callback like this. You really ought to just change the interface and return the promise back to the caller so the caller can just use .then()
and .catch()
on the promise directly.
Run them sequentially without async
and await
:
Or, if you want to run them sequentially (not in parallel) without using async
and await
, then you can do it the old-fashioned way for an asynchronous iteration (what we did before async/await
):
function run(sidArr, callback) {
let index = 0;
let results = [];
function next() {
// check if we're done and call callback
if (index >= sidArr.length) {
// ensure callback is always called asynchronously
// even if sidArr is empty
setImmediate(() => callback(null, results));
return;
}
// still more to process
client
.messages(sidArr[index ])
.update({ body: '' })
.then((message) => {
// save result and do next iteration of loop
results.push(message.sid);
next();
})
.catch(callback);
}
next();
}
run(sidArr, putYourCompletionCallbackHere);
Run them sequentially with async
and await
:
For completeness, here's how you would run them sequentially when you can use async
and await
:
async function run(sidArr, callback) {
try {
let results = [];
for (var i = 0; i < sidArr.length; i ) {
let data = await client.messages(sidArr[i]).update({ body: '' });
results.push(data.sid);
}
callback(null, results);
} catch (e) {
callback(e);
}
}
run(sidArr, putYourCompletionCallbackHere);
CodePudding user response:
The update
method returns a Promise
. Loop over the sidArr
array and call the messages
and update
method for each item in the array. Store the returned Promise
in a new array.
You can wait for all promises in an array to finish with the static Promise.all
function. The result will be an array of all resolved promises.
Then loop over each result once more to extract the sid
property.
exports.handler = function (context, event, callback) {
const client = context.getTwilioClient();
const sidArr = [];
const cc = event.ccSid;
const exp = event.expSid;
const cvc = event.cvcSid;
sidArr.push(cc, exp, cvc);
const requests = sidArr.map(message =>
client.messages(message).update({ body: "" })
);
Promise.all(requests)
.then(messages => messages.map(({ sid }) => sid))
.then(sids => callback(sids))
};