Singleton Promises

How to work with singletons that have asynchronous constructors.

thomas michael wallace
tomincode
2 min readAug 2, 2017

--

It’s a common pattern to have an asynchronous connection to a third-party-service function. It makes your library a good neighbour, as you don’t require everyone to wait around while you get your life together. It does, however, make working in parallel a bit more of a challenge. And that’s today’s lesson.

I needed to perform a set of writes to mongodb in parallel. Like all good service providers, it has a very asynchronous driver. It reads asynchronously, it writes asynchronously, and, importantly, it connects asynchronously. The asynchronicity of the connection, however makes it a bit of a pain to work with in parallel.

You see, if you have a ‘write-all-the-things’ function (i.e. await (await connect()).writeTheThings()) and you run it in parallel, you end up spawning multiple connections; something that my local mongodb (at least) doesn’t like. Chances are, too, what you intended was to create a shared connection, which you can then run parallel write requests against.

At this point you need a singleton Promise.

The trick is to remember what a Promise really is in JavaScript. For our purposes it’s a state machine that is either ‘pending’ and has to be waited on, or it’s a result that’s been returned. Our getDb() function uses a ‘singleton promise’; returning a new Promise in the first instance, or a cache of the original. When getDb() is called and await'ed on (e.g. ‘resolved’) it either halts until the promise resolves, or returns the already resolved result.

In the context of the above example- the firstwriteSomeTheThings worker will start forming the connection and wait. For a while, subsequent writeSomeTheThings will block while the database connection is being established. Once connected these workers will then continue; and all subsequent writeSomeTheThings workers will run immediately.

All the single(ton) promises- put your hands up!

--

--