Giter Club home page Giter Club logo

proposal-symbol-thenable's Introduction

Symbol.thenable

Champions: Myles Borins, Jordan Harband

Author: Gus Caplan

Stage 0

Why

The behaviour of Promise.resolve includes checking for then functions on whatever you pass it. This allows interop with legacy third-party Promise implementation protocols, and in most cases does not result in any issues, but there remain scenarios where it is unwanted.

This is especially true with dynamic import of module namespace objects, which will become part of the language spec very soon. A namespace might be interpreted as a "thenable" resulting in a non-namespace return from import() - which may be very unexpected. There has been a lot of previous discussion around these scenarios, including tc39/proposal-dynamic-import#47 and tc39/proposal-dynamic-import#48.

import * as static from 'X'

import('X').then((dynamic) => {
  assert(static === dynamic); // might be false, /hopefully/ the consumer of X
                              // and the author of X are both aware of this
                              // behaviour (and if they are, no one exploits
                              // it to do confusing things)
});

Proposal

The natural conclusion of this is to add some sort of modifier to an object such that Promise.resolve is aware that it should not perform "thenable" behaviour.

Enter: Symbol.thenable

Promise.resolve({
  [Symbol.thenable]: false,
  then() { return 'a time that isn\'t now'; },
}).then((o) => {
  o.then() === 'a time that isn\'t now';
});

In addition this symbol would be set by default on Module Namespace objects. While users can already import * a namespace object and return it through a promise resolution, this can still be considered an incredibly rare case today.

This came from trying to think of the most generalised solution to this problem. Alternatives could include explicitly blacklisting a Module Namespace Object in Promise.resolve, however there was resistance to this, as it might not be entirely intuitive why this object was being treated differently. Symbol.thenable provides clear explanation for that.

In the future, this symbol can also be used by a protocol (see the First-Class Protocols Proposal) along the lines of Promise.Thenable as a sort of "disabled" symbol.

Alternatives and Options

  • Mount symbol on Promise namespace (Promise.thenable)

  • Whitelist specific objects we don't want to treat as "thenables":

    1. If _resolution_ is a Module Namespace Object, then
      1. Return FulfullPromise(_promise_, _resolution_).
    

proposal-symbol-thenable's People

Contributors

devsnek avatar guybedford avatar robpalme avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

proposal-symbol-thenable's Issues

Pros/cons of symbol on object with then method vs on method

It seems that opting out of being treated as thenable is mostly necessary when the presence of the then method is coincidental -- there is a method named then but its purpose is not related to promise resolution.

If that's the case, then the opt-out is necessary because of something about the then method, not something about the object.

Maybe document why/whether adding a marker symbol to the object is preferable to adding a marker symbol to the then method.

No need for Symbol to be visible on Module Namespace

A variety of Symbols can be used to change behaviors but are not always exposed / used for exotic objects:

class X {};
Object.setPrototypeOf(X, null);

// undefined, true
console.log(X[Symbol.hasInstance], new X() instanceof X);

Viable?

Given the abundance of userland promise libraries, is there really a feasible way to un-poison "then"? Having some consumers treat a value as thenable and others not seems worse than just abandoning the property name, placing it in a class with "__proto__" and (in DOM land) form element names that shadow <form> element properties.

Performance implications

It's been very complicated and tricky to optimize the performance of Promises in JavaScript engines. This proposal adds another Get to the Promise resolution path, which could degrade performance further. Is it worth that cost, when there are alternatives like special-casing Module Namespace objects? cc @gsathya

Changing the then-method key

I think there should be a way to configure the key Promise.resolve tries to use for the then-method, instead of simply opting out. Then you could instruct Promise.resolve to check another property and still use 'then' for whatever purpose.

Alternative Proposal: Extend `import` syntax to allow boxing of the module

While Symbol.thenable might be more widely useful than just solving the case of import() for the most part the .then-able issue can be solved by wrapping the promise in a trivial object e.g.: { value: somePromise }.

This can't be done for import() (generally even if data: URLs might work in the browser) as the Promise is always created by the host. So I'd propose that a mechanism directly for import() is created that performs this boxing.

I suggested a few mechanisms back in the original issue about dynamic import thenables which could be used for solving this particular case.

How to use this with thenable objects?

The example talks about a .then() method that return a Date instead of a Promise, but how could I do the same when it return a Promise, but don't want to consider it a thenable object?

A real life example (a pitfall I've fallen several times) is when I construct an object that would last some time until it's ready or can fail (for example a network connection) so I add a .then() method on the class resolving to the object itself when it's ready to be used. Problem is, since I'm resolving to a thenable object (the object itself), the .then() method is called again, and I end up in an infinite loop.

class A {
  then(onResolve){
    onResolve(this)
  }
}

new A().then(function(instance){
  // never called
})

Only solution I've found is to remove the then() method once it's resolved, or don't return the this instance. How could this be fixed? Setting the symbol would not prevent to be used as a thenable anymore? Maybe an isNotThenable flag could be added to Promise.resolve() to don't check if the resolved value is a thenable object? This way onResolve would be set to the promise returned by Promise.resolve(), that resolve to a non-thenable object:

class A {
  then(onResolve){
    onResolve(Promise.resolve(this, true))
  }
}

new A().then(function(instance){
  instance.constructor === A  // true

  return instance.then(function(){
    // this should still work since `then` is called directly
    return 'blah'
  })
})
.then(console.log)  // 'blah'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.