question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Destructuring doesn't work with await expression

See original GitHub issue
JSC_CANNOT_CONVERT: This code cannot be converted from ES6. Undecomposable expression at line 3 character 18
  const [first] = await Promise.all(['first', 'second']);
                  ^

This doesn’t work (link):

async function run() {
  const [first] = await Promise.all(['first', 'second']);
  return first;
}

run().then(first => console.log(first));

This works (link):

async function run() {
  const array = await Promise.all(['first', 'second']);
  const [first] = array;
  return first;
}

run().then(first => console.log(first));

Probably related: #2330

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:7 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
shickscommented, Aug 10, 2017

Sorry it’s taken so long to address this - was sort of hoping somebody else would pick it up.

There are three transpilations happening here: async-await, destructuring, and then finally generators (in that order). After async-await and destructuring, we end up with:

var a = $jscomp.makeIterator(yield Promise.all(["first", "second"])), first = a.next().value;

In order to transpile this, the RHS needs to be decomposed - specifically, I believe the $jscomp.makeIterator(...) expression needs to be “moved” to a separate variable that can be called after the yield returns. According to ExpressionDecomposer 1, the condition for this is as follows

A subexpression is MOVABLE if it can be replaced with a temporary holding its results and moved to immediately before the root of the expression. There are three conditions that must be met for this to occur:

  1. There must be a location to inject a statement for the expression. For example, this condition can not be met if the expression is a loop condition or CASE condition.
  2. If the expression can be affected by side-effects, there can not be a side-effect between original location and the expression root.
  3. If the expression has side-effects, there can not be any other expression that can be affected between the original location and the expression root.

It looks like the issue is maybe (2), though I’m a little shaky on the exact details here. The expression $jscomp.makeIterator can be affected by side-effects, and there’s no guarantee that there are no side effects while yielding. To further investigate, I simplified the expression quite a bit:

/** @const */
var x = x || {};

/** @const */
x.makeIterator = function(arg) { return arg; }

function* f() {
  var a = x.makeIterator(yield);
}

This code still fails to transpile with the same error. (It’s worth nothing that $jscomp.makeIterator isn’t actually declared as @const - not sure if this matters, or if the compiler can infer that it is effectively const; either way, I did it with x.makeIterator and still got the error, so maybe it’s irrelevant). If I change makeIterator to a simple function, rather than a qualified name, then it works fine, presumably because it was the GETPROP that “can be affected by side effects”.

I don’t understand why the compiler doesn’t recognize that this is never reassigned, and therefore is actually not affected. And I think we still have some significant issues with generator transpilation, since (as far as I can tell) other transpilers seem to be more permissive here - maybe this should be a suppressible warning, rather than an error? In any case, it seems like a quick fix would be to change how destructuring is rewritten, to save $jscomp.makeIterator into a temporary variable in a separate statement (or possible the root of the function) before calling it. That would at least fix this unfortunate interaction.

0reactions
lauraharkercommented, Jul 11, 2019

In this particular case it is safe to ignore the warning. In general, you should pay attention to the warning if you are accessing a constructor or class off of an aliased namespace, or aliasing a constructor.

Here’s a snippet of the generated code that’s triggering the warning (source):

    if ($jscomp$generator$context.nextAddress == 1) {
      JSCompiler_temp_const = $jscomp;
      JSCompiler_temp_const$jscomp$1 = JSCompiler_temp_const.makeIterator;
      return $jscomp$generator$context.yield(Promise.all(["first", "second"]), 2);
    }
    $jscomp$destructuring$var0 = JSCompiler_temp_const$jscomp$1.call(JSCompiler_temp_const, $jscomp$generator$context.yieldResult);

The compiler is warning you that JSCompiler_temp_const = $jscomp; may be unsafe.

In this case, it’s actually safe. The only usage of JSCompiler_temp_const is to access JSCompiler_temp_const.makeIterator, and the compiler will not unsafely flatten $jscomp.makeIterator because it is not an enum or constructor.

Note: I might have missed a few edge cases since the property flattening logic is generally confusing. I filed https://github.com/google/closure-compiler/issues/3428 to avoid false positives like this one.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Does desctructuring assignment not work the same with async ...
As demonstrated in the live code snippet below, the answer is yes. Destructuring assignments work on the results of awaited function calls ...
Read more >
Destructuring assignment - JavaScript - MDN Web Docs
The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from ...
Read more >
ES2015 Destructuring & More - Medium
ES2015 Destructuring doesn't have to be scary, and in this post we'll go in-depth as to how it works, the various patterns you...
Read more >
Tricks with JavaScript Destructuring - DigitalOcean
If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the...
Read more >
Destructuring Assignment | Advanced JavaScript
We must be careful when destructuring arrays to make sure that we don't ... the desired default value in the left-hand side of...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found