-2

I need to write some Cloud Functions in nodejs. This particular Cloud Function needs to iterate over an array of URLs and make some asynchronous calls to a third-party API.

Here is some placeholder code that simulates what I'm trying to do, adapted from here:

function checkPhotos(photos) {
  var checkedPhotos = [];
  var promiseArray = photos.map(asyncFunction);

  Promise.all(promiseArray).then(results => {
    console.log('results are: ', results)
    results.forEach(result => { checkedPhotos.push(result) });
  })

  // I want to block here until the Promise.all call above completes
  // but it does not block, it returns [] immediately while the async
  // calls run
  return checkedPhotos;
}

// This is a pretend async function to simulate the third party API
function asyncFunction(photo) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("in async function, photo is " + photo)
      resolve(photo)
    }, Math.random()*2000)
  })
}

Then I call the functions:

var photos = ['https://example.com/example.jpg', 'https://example.com/example2.jpg'];
var checkedPhotos = checkPhotos(photos);

I expect checkedPhotos to contain the values from results but it is an empty array at first, then the asynchronous calls complete and the array gets populated. How do I make checkPhotos() block until all the asynchronous calls are complete so I can return checkedPhotos as a fully populated array?

anothermh
  • 9,815
  • 3
  • 33
  • 52
  • You don't "block" in Javascript. You place the code that needs the results in the asynchronous callback. That's where the data is available. With promises, you can also use `async` and `await` to write more synchronous-looking code, though it's still not really blocking. – jfriend00 Aug 29 '19 at 03:18
  • this is how you would use promises correctly - https://pastebin.com/rNQrS1x5 – Jaromanda X Aug 29 '19 at 03:18
  • 1
    the main thing you need to know about any asynchrony in javascript is ... you can't "short circuit" the code into giving you synchronous results ... for promises, you must use `.then` to get the result ... or use async/await syntax – Jaromanda X Aug 29 '19 at 03:22

3 Answers3

3

I would suggest just return Promise.all because you are not doing anything inside function. If you want to perform any action use async/await to wait for the result.

async function checkPhotos(photos) {
    var checkedPhotos = [];
    var promiseArray = photos.map(asyncFunction);

    const results = await Promise.all(promiseArray);
    console.log('results are: ', results)
    results.forEach(result => { checkedPhotos.push(result) });
    // Some other action
    return checkedPhotos;
}

Without action

function checkPhotos(photos) {
  var promiseArray = photos.map(asyncFunction);
  return Promise.all(promiseArray);
}

async function somefunc(){
    await checkPhotos(photos);
} 
Rahul Sharma
  • 9,534
  • 1
  • 15
  • 37
1

In your example you have to return values inside Promise.all callback function, because Promise.all will subscribe on that asynchronously calls and asking db engine to run the callback function once it done,. Also you need to subscribe on catch function in Promise.all.the(cb).catch(cb) and pass call back function Incas of errors also return something.

An alternative way to wait for all promises to be done, you have to use async and await keywords, just add async keyword before function checkPhotos(... and ads await keyword before Promise.all... , that's will tell js engine that you need to run the code below await line when you got the response.

If you're calling this function form another function, you need other function to wait for this to be resolved you can simply use the upper async await solution and on call subscribe on this function call back like this.

 function other () {
  checkPhotos(photos).then(cb);
 }

   async function checkPhotos(photos) {
     .....
      await Promise.all(....then()..)
         // Do some stuff
        return checkedPhotos
       }

That's how js handle asynchronous calls, it will not block you code ( event loop ), it just allow you to subscribe on an asynchronous call and pass a callback function to be executed once JavaScript get a response.

Yasser Mas
  • 1,652
  • 1
  • 10
  • 14
  • Yes offcours, I know it's case sensitive, it's just typo, thanks. – Yasser Mas Aug 29 '19 at 07:29
  • Ok, I didn't think that, just because you've made that typo 5 times... But now everything's OK. I've retracted my downvote. – FZs Aug 29 '19 at 07:35
-1

Why not use async await?

async function checkPhotos(photos) {
  var checkedPhotos = [];

  for (const photo of photos) {
    let result = await asyncFunction(photo);
    checkedPhotos.push(result);
  }

  return checkedPhotos;
}

Then call it like this:

var photos = ['https://example.com/example.jpg', 'https://example.com/example2.jpg'];
checkPhotos(photos)
.then(checkedPhotos => {
  // do something
})
JBaczuk
  • 13,886
  • 10
  • 58
  • 86
  • 1
    now this code returns `undefined` - this is worse than the code in the question – Jaromanda X Aug 29 '19 at 03:20
  • I think the promise.all better than loop, promise.all will fire all asynchronous call in parallel and wait for it, your solution will wait for each one before sending an asynchronous request, that's will cause too much delay. – Yasser Mas Aug 29 '19 at 03:50