2

For example, I have function which Promise.resolve() if I already have any cached entity id else it make ajax call to reserve entity id and then Promise.resolve() new entity id

function getReservedEntityId(collectionName) {
        //if (!haveCachedIds) {
            //make ajax call to reserve new ids
            Promise.resolve(newId);
        }
        return Promise.resolve(cachedId);
};

How can we synchronously call it multiple times to reserve multiple entity ids?

P.S. I know that the correct approach is to make this function take parameter that will specify the count of entity ids and make request and return ids accordingly but I wanted to understand how can we call synchronously multiple times any function which is returning promise.

Raza
  • 230
  • 1
  • 4
  • 11
  • http://stackoverflow.com/a/5188020/7462678 – LS_85 Jan 25 '17 at 11:58
  • 1
    That answer is old and doesn't mention how to do this using promises. The methods described in that answer are valuable for historical purposes, but not for writing modern code. – fvgs Jan 25 '17 at 12:00
  • What do you mean by "synchronously call"? Clearly you can't take one asynchronous operation, and expect it to become synchronous just by repeating it – Eric Jan 25 '17 at 12:18
  • All code paths need to return a promise. Simple create an array on pending promises for the ammount of times you want to execute your function. Then pass that array into Promise.all. That will resolve once all of your pending promises resolve. If each call don't need data from the other it would be quicker for the end user to batch them like this async. – ste2425 Jan 25 '17 at 21:48
  • @fvgs I will accept it after trying it out. Sorry for little delay. I'm currently busy right now but I will try this before accepting your answer. Thanks a lot for the detailed answer. – Raza Jan 30 '17 at 11:36

2 Answers2

2

First, the implementation of getReservedEntityId() needs to make correct use of promises. I recommend a thorough reading of how promises work. In particular, it's important to understand that when your function performs an asynchronous task, you need to return a promise that will either resolve or reject based on the result of the asynchronous task.

function getReservedEntityId(collectionName) {
  if (haveCachedIds) {
    return Promise.resolve(cachedId);
  } else {
    return new Promise((resolve, reject) => {
      // Make the AJAX call and call resolve(newId) in the success callback
      // or call reject(errCode) in the failure callback.
      // The arguments newId and errCode can be any values you like and are
      // the values that get passed to the next link in the promise chain
      //   i.e. the values passed to then() or catch()
    });
  }
}

With that squared away, there are two recommended ways to make the calls synchronous:

1) Utilize a promise chain

getReservedEntityId(collectionName)
  .then((id) => {
    // Probably want to do something with `id` first...

    return getReservedEntityId(collectionName);
  })
  .then( ... )
  .then( ... );

Of course, if you're going to pass the same function to each .then() call, you could just as well declare it as a regular function so as to not repeat yourself.

2) Using async/await

This is a new ES2017 feature and is still not widely supported. As of the time of this writing, Node.js supports async/await with the --harmony flag, but most browsers do not. That said, async/await is intended for this exact purpose, treating functions returning promises as though they were synchronous. If you want to start using async/await in your code now, it is common practice to use JavaScript transpilers which which transpile your future-ready JavaScript to code that is supported by all major browsers.

This is how you would use async/await:

(async function someAsyncFunction {
  const id1 = await getReservedEntityId(collectionName);
  const id2 = await getReservedEntityId(collectionName);
  const id3 = await getReservedEntityId(collectionName);
                          .
                          .
                          .
})();

The syntax is much nicer and more readable than the promise chain because it's designed for this exact purpose. Note that I have made the function self-invoking here so that it matches your behavior without having to make an extra function call. But you can use and call a function defined with async function just like any other function that returns a promise.

fvgs
  • 21,412
  • 9
  • 33
  • 48
  • Thanks @fvgs. Sorry my mistake I didn't correctly write the example code but I do know the proper format of promise and when to resolve and reject any promise. you answer is quite helpful, I do have promise chain in my mind but I didn't tried it to make sure it can work or not What do you mean by _you could just as well declare it as a regular function so as to not repeat yourself_? – Raza Jan 25 '17 at 12:25
  • What I meant is that if you intend to have a promise chain with several `.then()` calls, all of which do the same thing, you could just define a function that does that "thing" and pass the name of that function to each `.then()` call instead of writing out an arrow function for each `.then()` call. I was just expounding the most basic DRY principle for the sake of thoroughness. I'm sure this is something you're well aware of. I was just indicating it for any reader who might overlook it if they're new to promise chains. – fvgs Jan 25 '17 at 12:33
  • Worth noting that `someAsyncFunction` will return a promise and not a "normal" value, so can't quite be used "just like any other function" – Eric Jan 25 '17 at 13:47
  • That's an excellent point to clarify, thanks for bringing it up. I've edited the answer to make it clear the async function returns a promise. – fvgs Jan 25 '17 at 21:39
  • @fvgs I'm using it on front-end code so I can't use async/await while I am trying promise chain and trying to dynamically generate number of .then() calls. – Raza Feb 09 '17 at 10:17
0

@fvgs your answer is also correct. But here's the complete solution and the challenge which I have faced is to maintain list of reserveIds which was in response of every getReservedEntityId call.

getEntityIds: function (entity, count) {
    if (!count || count == 1)
        return Utils.getReservedEntityId(entity);

    var promise = new Promise(function(resolve, reject) {
        var result = [];
        var chain = Utils.getReservedEntityId(entity);
        var callBack = function CB (result, newId) {
            result.push(newId);

            if (result.length == count)
                resolve(result);
            else
                return Utils.getReservedEntityId(entity);
        }.bind(null, result);

        for (var i=1; i <= count; i++) {
            chain.then(callBack);
        }

        chain.catch(reject);
    });

    return promise;
}
Raza
  • 230
  • 1
  • 4
  • 11