2

I am writing a JavaScript library for the browser that currently exposes functions that take callback arguments. For the next version of the library I would like to have those functions return promises instead. However, I haven't figured out a good way to know which flavor of promises (if any) exist in the client app.

I could say my library requires the use of a Promise polyfill, but promise libraries like Bluebird, Q, and RSVP don't always create a Promise global. So then apps that depend on my library would have to either add a redundant promise polyfill, or expose their library's implementation to the global namespace like

import RSVP from 'rsvp';

window.Promise = RSVP.Promise

I'm having a hard time finding any examples of a good way to avoid including redundant promise implementations. The closest thing I've seen is https://github.com/agershun/alasql/blob/develop/src/18promise.js, which will use the app's global promise if it exists, but also includes an inlined copy of the es-6-promise polyfill.

Tom Wayson
  • 1,187
  • 1
  • 12
  • 21
  • 1
    You could require your caller to pass in a Promise implementation into your lib's constructor, or have a global `MyLib.init(PromiseConstructor)` that only needs to be called if they a polyfill is required – Ruan Mendes Feb 13 '17 at 21:20

1 Answers1

2

There are basically three situations you have to deal with:

1. There is a global Promise already defined and the caller of your library does not provide a different one. In that case, you should be able to detect the existing global implementation and just use it without any other intervention. This is what the world looks like in modern browsers today and into the future so this is a good case to handle well.

2. There is no global Promise defined and the caller of your library does not provide one to you. If you intend to support this case, then you need to either have a polyfill already built-in or be able to dynamically load one. I would recommend dynamically loading Bluebird since it is 100% ES6 compatible, is available via a CDN (easy to dynamically load), dynamically loading would avoid duplicate implementations and Bluebird has many other useful benefits too.

3. The caller of your library wants to provide you a specific promise implementation that they have already loaded. This other implementation may or may not be defined as the global Promise. In this case, you would usually have an optional setting or init for your library that allows the developer to tell you which promise library to use before any of your other API functions are called. You store that away in your own storage and just use that everywhere in your code.

You can be very developer-friendly and flexible by supporting all three of these options. It requires only a little code on your part. First, you offer an API for setting which Promise implementation to use and you store that implementation in your module and you use that everywhere. Then, if that API is not called to set a specific implementation, you detect if the global Promise is there. If it is, you use it. If not, then you dynamically load Bluebird from a CDN. That's a fairly small amount of code for very flexible and developer-friendly support and, going forward, it's nice and simple for everyone (you will just default to the build-in ES6 Promise implementation).

If you want the bare minimum (and small) ES6 polyfill, there are several available. One I've seen regularly is this one which comes from the RSVP folks, but is designed to be as small as possible 100% compatible ES6 polyfill with no extra features (only 2.6k).

As an even simpler alternative for you (but less developer-friendly), you could put more of the burden on the developer using your library and just declare that your library requires that a global Promise be defined before initializing your library. In that case, the developer using your library would have to get their own Promise polyfill if they wanted to use your library with older browsers. This obviously puts more of the work back on the developer using yourlibrary and you'd have to check for the presence of a global Promise and, if not present, then throw an exception upon initialization. I personally would not recommend this alternative.

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979