Introduction of promise.return() and promise.throw() similar to generator.return() and generator.throw()

James Browning thejamesernator at gmail.com
Sat Jan 14 11:23:35 UTC 2017


If you really need an object that you describe then you use a function
like this to create them:

```js
function deferred(executor=() => {}) {
    let _resolve
    let _reject
    let promise = new Promise((resolve, reject) => {
        _resolve = resolve
        _reject = reject
    })
    promise.return = _resolve
    promise.throw = _reject
    return promise
}
```

and you can use it as such:

```js
const p = deferred(resolve => setTimeout(resolve, 1000, "Hello, world!")
p.then(result => console.log(result)) // Will display Interrupt not
Hello, world!
// resolve early, no need for preventDefault as only first call to
resolve is considered
p.return("Interrupted")
```

Something to be aware of is generator `return/throw` is not about
Promises, its about sending values to and from a generator to allow
two way communication with a execution engine (e.g. creating
applications which can be ported to different environments)

```js
function* app() {
    yield ['changeBackground', 'green']
    yield ['wait', 1000 /* milliseconds */]
    yield ['changeBackground', 'aquamarine' /* missplet */]
}

function engine() {
    const iter = app()
    function step() {
        const { value, done } = app.next()
        if (done) {
            return // Nothing left to do
        }
        else {
            if (value[0] === 'changeBackground') {
                if (!colors.includes(value[1])) {
                    iter.throw(new Error("Color not found"))
                }
                // ...etc
       }
}
```

In generators you can respond to values, errors, and returns created
from the outside, even try-finally isn't something special for
generators, regular functions can skip return with try-finally too
e.g.:

```js
function foo() {
    try {
        return "Hello"
    } finally {
        return "Goodbye"
    }
}

console.log(foo()) // Goodbye because return "Hello" was overridden by finally
```

Promises aren't generators, `async functions` aren't either, although
a polyfill can use generators to implement correct async functions by
replacing `await` with `yield` and wrapping them with a spawn
function, however thats a polyfill detail, and the polyfill almost
certainly won't replace `await foo` with `yield AwaitResult(foo)`
because that's trivially hand-able with promises themselves (see the
officially suggested polyfill:
https://tc39.github.io/ecmascript-asyncawait/#desugaring).

Generally you won't need anything like Q.deferred, you can just
transfer the resolve/reject functions to wherever you need them using
either closures or objects.


On 1/13/17, Igor Baklan <io.baklan at gmail.com> wrote:
> Yes, it's good note, that it can be "too public" and may be some one would
> like to prevent external "intrusion" into it's "private state", but it can
> be easily solved by the means of a wrappers, that wraps some private
> ``target`` and delegate to it only "safe calls", that will not
> interrupt-it/cancel-it, it can look like
> ``privateSattePromise.preventAbnormalCompletion()`` - which returns some
> wrapped promise with more "safe" behavior, then this promise can be shared
> with "wider audience". But from the opposite side - if somebody make some
> internal logic, and do not intend to return that internal objects
> "outside", why he should complicate his life so much (and do not "trust
> himself" if both methods - which produces some promise and consumes that
> promise - are private in some place)? As an example of such kind of
> wrappers I can offer you [``java(Collections#unmodifiableList)``](
> https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#unmodifiableList(java.util.List)).
> It's conception is very simple - it takes some mutable list and wraps it by
> some wrapper which prevents modification, as a result you'll obtain some
> "semi-immutable" list, meaning that you will not be able modify it through
> that wrapped instance, but still can do some changes over initial
> collection, and that changes will be quite visible through the wrapper.
>
> So from my point of view, it can be 2-wo approaches - always expose that
> "external intrusion api", and the explicitly "hide" that api by the means
> of some ``promise.giveMeSafeWrapper()``-like api. Or an other option, just
> do not expose that api by default, and say that if you want to create that
> "extra public" promise you should use some subclass of regular promise,
> like ``extraPublicPromise = new Promice.ExtraPublic(executor)``, and the
> ofcourse, it would be more explicit, which stuff is internal, and should be
> wrapped before giving it somewhere outside, and knowing that all the rest
> is "safe" in terms of "external intrusion".
>
> But denying that capability at all, being to lazy for writing
> ``.giveMeSafeWrapper()``-like calls is also not very good choice (as for
> me). (By the way, approach given in
> [the-revealing-constructor-pattern#historical-origins](
> https://blog.domenic.me/the-revealing-constructor-pattern/) doesn't looks
> "so insane". Considering ``deferred = Q.defer();`` and ``p =
> deferred.promise;``, I see that there are some public part and private part
> of that object. And if they for example share most of api on both of them,
> but private instance has "alittle bit more" available api for control, then
> in would be not so bad idea. Again, not assuming that using private
> instance for task completion should be "normal api", but as some sort of
> "abnormal completion api" it can be quite useful).
>


More information about the es-discuss mailing list