100

One can await a non-Promise and that's good so.

All these expressions are valid and cause no error:

await 5
await 'A'
await {}
await null
await undefined 

Is there any detectable effect of awaiting a non-Promise? Is there any difference in behavior one should be aware of to avoid a potential error? Any performance differences?

Are the following two lines completely same or do they theoretically differ?:

var x = 5
var x = await 5

How? Any example to demonstrate the difference?

PS: According TypeScript authors, there is a difference:

var x = await 5; is not the same as var x = 5;; var x = await 5; will assign x 5 in the next tern, where as var x = 5; will evaluate immediately.

ttulka
  • 10,309
  • 7
  • 41
  • 52

3 Answers3

112

await is not a no-op. If the awaited thing is not a promise, it is wrapped in a promise and that promise is awaited. Therefore await changes the execution order (but you should not rely on it nevertheless):

The following outputs 1, 2, 3:

console.log(1);

(async function() {
  var x = await 5;
  console.log(3);
})();

console.log(2);

With the await removed it's 1, 3, 2:

    console.log(1);

    (async function() {
      console.log(3);
    })();

    console.log(2);

Additionally await does not only work on instanceof Promises but on every object with a .then method:

await { then(cb) { /* nowhere */ } };
console.log("will never happen");

Is there any detectable effect of awaiting a non-Promise?

Sure, .then gets called if it exists on the awaited thing.

Is there any difference in behavior one should be aware of to avoid a potential error?

Don't name a method "then" if you don't want it to be a Promise.

Any performance differences?

Sure, if you await things you will always defer the continuation to a microtask. But as always: You won't probably notice it (as a human observing the outcome).

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • 4
    _"but you should not rely on it nevertheless"_ Why not? – a better oliver Apr 24 '19 at 19:09
  • 5
    @aBetterOliver example: you start two very short running actions and want to work with their result. You know that action 1 finsihes earlier than action 2, so you store the result of 1 in a variable, and access that when 2 is done. Now someone somewhen changes the algorithm, so that 1 is slower than 2. Your code breaks and no one knows why. If you have to execute async tasks in a specific order, you should write that down explicitly. – Jonas Wilms Apr 24 '19 at 19:14
  • 1
    @JonasWilms Your example does not relate to your statement. You stated that one should not rely on the fact that await changes the execution order. This is wrong, we rely on it all the time (i.e. when exploiting the atomicity of synchronous blocks). Your example is about **two** async tasks and one relying on the empirical (yet not guaranteed) execution time of the other, which is just a general observation of parallel programming: *You cannot rely on the order of incomparable transactions in the precedence graph* – One Full Time Equivalent Jun 09 '22 at 07:58
  • 1
    I disagree that the first example parallels (pardon the pun) the OP's example. To do so you'd have to await the console log, `await console.log(3);`, in which case there is no *observable* difference in *behavior*. – Lawrence Dol Jan 17 '23 at 21:02
30

Completely agreed with Jonas's statements. One thing that was not answered in his question was Are the following two lines completely same or do they theoretically differ?:

following two lines are not completely same, they're theoretically different.

  1. var x = 5
  2. var x = await 5

execution time in my console for 1st and 2nd statement is 0.008056640625ms and 0.055908203125ms respectively. async/await, setTimeOut etc are APIs provided by Run time in which JavaScript Run time is running. Putting await on a non-promise will be executed in event-loop. Line 1 will be executed right after reaching the stack but the line 2 will take few time(milliseconds) as it will 1st go to the stack and then to the task queue after skipping webAPI waiting section because there's no promise to be resolved & finally after that control will be given to stack again for execution.

fahad tufail
  • 555
  • 4
  • 10
  • 1
    I think this question was already answered with the example code snippet. Thank you anyway! – ttulka Mar 21 '19 at 11:19
  • 2
    actually the last part of the question was missing though i.e **Are the following two lines completely same or do they theoretically differ?**. – fahad tufail Mar 21 '19 at 12:23
  • The example code from @JonasWilms showed the difference very clearly... – ttulka Mar 21 '19 at 12:28
  • 4
    great. assume it an other explanation then :) – fahad tufail Mar 21 '19 at 12:44
  • 1
    This is an important distinction, because this behavior was "fixing" a bug for us implemented by a junior engineer that didn't realize the function they were `await`ing wasn't a promise and thus shouldn't be doing anything. "But it fixes the bug!" (a race condition bug that was "solved" by putting the non-promisified function onto the event loop... *after* other events) they had said. Little did we know none of us knew about this behavior of `await`.... – Caleb Jay Aug 04 '22 at 03:31
  • But will it be synchronous. Can I be guaranteed that, in your example, a line 3. will never get executed before line 2. is complete? – Brobic Vripiat Nov 26 '22 at 19:15
0

The awaited code gets wrapped in a promise if the thing awaited does not have a then property. Therefore, though there is a difference in performance (it takes longer), there is no observable difference in behavior unless what you await has a then property -- in which case behavior might be unpredictable if the thing awaited is not actually a promise which will complete.

Lawrence Dol
  • 63,018
  • 25
  • 139
  • 189