Trying multiple JavaScript promsies in sequence, until one succeeds

Trying multiple JavaScript promsies in sequence, until one succeeds

2 Aug 2022 javascript promises

I was recently working on a legacy project where a login request to a system needed to look for the user in up to three different instances of the same back-end system, hosted in different countries.

How to approach this? Promises are the answer, but using what strategy?

Hazy on promises? Check out my in-depth guide.

There's a couple of ways to do this. First, I'll show you the one I used, and then we'll look at an alternative.

Setup 🔗

First, let's set things up. For the purposes of this article and keeping things simple, we'll assume our back-ends return a string, either "yes" or "no" indicating whether the user was found.

const baseUrl = 'https://{instance}.example.com'; const instances = ['uk', 'aus', 'ita']; const user = 'foo'; const pass = 'bar'; const login = (user, pass, instance) => { return new Promise(async (res, rej) => { let result = await fetch( baseUrl.replace('{instance}', instance), { method: 'post', body: JSON.stringify({user, pass}) }); result == 'yes' ? res(instance) : rej(); }); }

So we set up our base URL, with an {instance} placeholder which will be swapped out for the actual instance subdomain we're calling (UK, Australia or Italy). Then we make a POST request to that back-end to try to resolve the user. Finally, we resolve or reject the wrapping promise (in the former case, passing along the successful instance name).

Solution 1 🔗

Remember, we don't know which instance the user's account lives on, so we'll just keep trying them, until one request succeeds. Here's how I did it:

for (let i of instances) try { await login(user, pass, i); localStorage('instance', i); break; //no need to try further instances } catch(e) { }

That's it, really - nothing too complex. If the back-end says 'yes', the promise is resolved and we store the host instance in local storage so the rest of the app can use it for making requests. We break at that point, since our work is done.

If the back-end says 'no', we're sent to the catch and we keep trying more instances, as long as any remain.

Solution 2 🔗

I mentioned there were a couple of ways to approach this. The second is to use Promise.any(). This method itself returns a promise, which is resolved when any of the sub-promises fed to it fulfills.

Check out my article on Promise.any() for a closer look at this later add-on to the Promises API.

The major change with this approach is we need to prepare an array of promises, one per instance, ahead of time:

let proms = instances.map(instance => login(user, pass, instance) );

Then we can just feed that array to Promise.any()!

Promise.any(proms).then(instanceFoundIn => { alert('Found user in instance '+instanceFoundIn); localStorage('instance', instanceFoundIn); });

I hope you've found this article useful!

Did I help you? Feel free to be amazing and buy me a coffee on Ko-fi!