The Promise of Async-Await

The other day, reviewing code, I encountered:

async function loadUrl(u) {
  var v;
  await fetch(u).then( (res)=>res.json() ).then( (js)=>{ v=js; } );
  updateUI(v);
}

I thought, “async-await, excellent stuff” I didn’t quite understand async-await, so I read up on it. The more I read, the more I realised this code exhibits a misunderstanding of async-await.

The first misconception I had was that an async function would somehow hold up execution until the answer was ready. What does this code print?

async function theValue() {
  let r = await Promise.resolve(42);
  return r;
}
console.log(theValue());

I had assumed, since the return is after the await, is would somehow magically pause at await, and not return from the function until the Promise had resolved to 42. I was wrong.

It returns a Promise.

Promise { <state>: "pending" }

What about this code?

async function theValue() {
  let r = await Promise.resolve(42);
  console.log(r);
  return r;
}
console.log(theValue());

This code will output a Promise object, and then output 42.

Promise { <state>: "pending" }
42

The order of this output is really important. The 42 is coming from the console.log(r) while the Promise comes from console.log(theValue()).

async-await is not a new feature of javascript. It’s just syntactic sugar, something that could be implemented in a macro-supporting language.

It works something like this:

async function X() {
	CODE1...;
    let ARG1 = await FN1(args);
    CODE2...;
    return RETVAL;
}

becomes:

function X() {
  return new Promise( 
    function(resolve) {
  	  CODE1...;
      resolve(FN1(args));
    }
  ).then(
    function(ARG1) {
      CODE2...;
      return Promise.resolve(RETVAL);
    }
  );
}

There’s some syntactic sugar to share variables between the various .then functions, but that’s essentially all that’s happening.

Which brings me back to the original code I was reviewing:

async function loadUrl(u) {
  var v;
  await fetch(u).then( res=>res.json() ).then( js=>{ v=js; } );
  updateUI(v);
}

The code smells because the author doesn’t really understand async-await, or promises. You can easily write this without async-await:

function loadUrl(u) {
  fetch(u).then( res=>res.json() ).then( js=>updateUI(js) );
}

Or you can write it using async-await:

async function loadUrl(u) {
  let res = await fetch(u);
  let js = await res.json();
  updateUI(js);
}

The mix of async and .then, though, makes me suspect the author just copied and pasted code from the web, without really understanding it. I think the async-await version is the easier to read, but the .then version is perfectly legible as well. The mixed version, though, just confuses.

the lateral alternative

32 years exquisite software