Home > Enterprise >  How to run a few API calls in a loop and then use callback function
How to run a few API calls in a loop and then use callback function

Time:09-21

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))
};
  • Related