Mukhtar Bahadory

How to promisify asynchronous Chrome extension API operations?

When working with Chrome extension APIs, nearly every operation you can perform is going to be asynchronous and ran on the event loop. This is really fine and dandy and a common pattern when working feature rich APIs, however it quickly gets out of hand when you have to do one asynch operation after another.

It's not that the code runs poorly or that it's difficult to conceptualize, at least if you understand asynchronous code, it's just that it becomes incredibly difficult to read.

Below is a really simple case of the native API of chrome.storage.sync.get. The item with the key of hello is queried for and when the result is available, the callback function provided is executed. Just imagine how messy it would get if we wanted to set hello to something else right after reading it.

chrome.storage.sync.get('hello', result =>
  console.log(result)
)

Simply the reading the statement "when the result is available, the callback function provided is executed" should scream at you that the event loop is being utilized and it's not simply just a callback function. Also noting that our potential problem is with the readability of writing such code repeatedly, it's pretty evident that we probably wanna start using async/await to get this operation done.

Unfortunately, the Chrome extension APIs don't provide you access to promises, but that's fine. We can make our own.

Note the below code sample. We return a promise which is resolved when the result is availabe and the callback function provided is executed by Chrome. You can apply this solution to any other Chrome or non-Chrome API.

const getStorageData = key =>
  new Promise((resolve, reject) =>
  chrome.storage.sync.get(key, result =>
    resolve(result)
  )
)

The readability of using the chrome.storage.synch.get API is significantly improved and your life is made much easier, enjoy.


Here is a version of the code sample with some error handling thrown in there (no pun intended o_o). chrome.runtime.lastError is some magic which is sure to point to the error caused by your invocation of a Chrome API, if any errors were caused.

export default  key =>
  new Promise((resolve, reject) =>
  chrome.storage.sync.get(key, result =>
    chrome.runtime.lastError
      ? reject(Error(chrome.runtime.lastError.message))
      : resolve(result)
  )
)