returning non-Promise values from async functions and running them synchronously (or Promise.sync() idea)

#!/JoePea joe at trusktr.io
Sun Feb 3 23:39:18 UTC 2019


I often find myself enjoying async functions until the time comes when I
don't want to await anything, and I want the call stack to be sync (i.e. I
don't want to defer if I don't have to).

But, async functions always return a Promise. So I find my self converting
my async functions back into normal functions so that I can return
non-Promise values, this way the caller can wait only if I return a
promise, otherwise continue synchronously.

Here's an example function in one of my classes:

```js
            initWebGL() {
                if (this.__glLoading) return false
                this.__glLoading = true

                // order of the if-else matters!
                if (this.__glUnloading) return
Promise.resolve().then(A.bind(this))
                else if (this.__glLoaded) return false

                A.call(this)

                function A() {
                    // ... load stuff (omitted for brevity) ...
                }

                return true
            }
```

then the caller (f.e. a subclass) only needs to wait when a promise is
returned:

```js
        initWebGL() {
            const superResult = super.initWebGL()
            if (superResult instanceof Promise) return
superResult.then(A.bind(this))
            if (!superResult) return false

            A.call(this)

            function A() {
                    // ... subclass loads stuff (omitted for brevity) ...
            }

            return true
        }
```

This is just one example, but in general I find many cases where I want to
run synchronously most of the time, and only sometimes have to defer
(whether by choice or not).

So I am losing out on the nice `await` syntax just because I can not do
what I want to do with `async`/`await`.

What if async functions could have some way to continue synchronously, and
return non-Promise values?

Or, what if we could have some sort of Promise API that would cause any
callers to synchronously away, like in the following example, so that the
change is not a breaking change, and async functions would still return
Promises but the Promise could perhaps continue synchronously:

```js
async function example() {
  return Promise.sync()
}
```

Then, in a caller, there would be no microtask deferral:

```js
async function test() {
  await example()
  console.log('one')
  // implicit return is a Promise.sync() here.
}

function main() {
  test()
  console.log('two')
}
```

The output in this case would be:

```
"one"
"two"
```

Note that in that example, we are _sure_ that `two` is logged after `one`
because those async functions only ever use `Promise.sync()`.

It would be good practice to use `await` regardless, because one may not
know if the async function they are calling will return a
non-`Promise.sync` value. So the `main` function is better written as

```js
async function main() {
  await test()
  console.log('two')
}
```

In this case, everything still happens synchronously, but if the author of
`example()` changes the implementation to return a non-sync Promise, then
things will still run in the expected order. F.e.

```js
async function example() {
  await fetch(...)
}
```

I keep finding scenarios where I want to avoid async unless I need async,
but then I lose the convenient async/await syntax.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190203/41588606/attachment-0001.html>


More information about the es-discuss mailing list