Skip to content

Legacy browser support: Issue with subclassing "Promise" #174

@rokoroku

Description

@rokoroku

Related to #51.
react-async is hard to use in legacy ES5 browsers due to the implementation of NeverSettle class. The problem is subclassing native Promise is not supported in well-known transpilers, and to achieve this we have to polyfill Reflect and use custom transpilers.

References:

Built-in classes such as Date, Array, DOM etc cannot be properly subclassed due to limitations in ES5 (for the transform-classes plugin). You can try to use babel-plugin-transform-builtin-extend based on Object.setPrototypeOf and Reflect.construct, but it also has some limitations.
https://babeljs.io/docs/en/caveats/#classes

Let me show an example:

  • source:

    // This exists to make sure we don't hold any references to user-provided functions
    class NeverSettle extends Promise {
    constructor() {
    super(() => {}, () => {})
    /* istanbul ignore next */
    if (Object.setPrototypeOf) {
    // Not available in IE 10, but can be polyfilled
    Object.setPrototypeOf(this, NeverSettle.prototype)
    }
    }
    finally() {
    return this
    }
    catch() {
    return this
    }
    then() {
    return this
    }
    }

  • transpiled (ES5)

var NeverSettle = (function (_super) {
    __extends(NeverSettle, _super);
    function NeverSettle() {
        var _this = _super.call(this, function () { }, function () { }) || this;
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                    ^ TypeError: undefined is not a promise

        if (Object.setPrototypeOf) {
            Object.setPrototypeOf(_this, NeverSettle.prototype);
        }
        return _this;
    }
    NeverSettle.prototype.finally = function () {
        return this;
    };
    NeverSettle.prototype.catch = function () {
        return this;
    };
    NeverSettle.prototype.then = function () {
        return this;
    };
    return NeverSettle;
}(Promise));
  • simple reproducible code about the error:
Promise.call(this, function () {}, function () {})
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
^ TypeError: undefined is not a promise
    at Promise (<anonymous>)

So I wish we can change the NeverSettle implementation rather than force us to set it up that complex setup. For example:

class NeverSettle {
  constructor() {
    const promise = new Promise(() => {}, () => {});
    this.then = promise.then.bind(promise);
    this.catch = promise.catch.bind(promise);
    this.finally = promise.finally.bind(promise);
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions