Understand promises before you start using async/await

What even is a promise?

I’ll keep this brief, since it’s been covered extensively elsewhere.

function getFirstUser() {
return getUsers().then(function(users) {
return users[0].name;
});
}
function getFirstUser() {
return getUsers().then(function(users) {
return users[0].name;
}).catch(function(err) {
return {
name: 'default user'
};
});
}

Cool… so how does async/await tie in?

Well, consider the above code. `getUsers()` returns a promise. Any promise we have, using ES2016, we can await. That’s literally all await means: it functions in exactly the same way as calling `.then()` on a promise (but without requiring any callback function). So the above code becomes:

async function getFirstUser() {
let users = await getUsers();
return users[0].name;
}
async function getFirstUser() {
try {
let users = await getUsers();
return users[0].name;
} catch (err) {
return {
name: 'default user'
};
}
}

Pitfall 1: not awaiting

The way I should call getFirstUser is like so:

let user = await getFirstUser();
let user = getFirstUser();

Pitfall 2: awaiting multiple values

So, here’s one of the rubs of await: under normal usage, I can only await one thing at a time:

let foo = await getFoo();
let bar = await getBar();
let [foo, bar] = await* [getFoo(), getBar()];
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
[getFoo(), getBar()]
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
  1. First. we dispatch `getFoo` and `getBar` and save the promises they return in `fooPromise` and `barPromise`.
  2. These actions are now in progress, they’re happening, there’s no stopping them or delaying them
  3. We await each promise in turn

Pitfall 3: your whole stack needs to be async

If I start using await somewhere, I now have the problem that it affects my entire stack. In order to call one of my async functions, ideally the caller itself should be an async function. This has a knock-on effect on my whole stack and makes it difficult to incrementally convert from callbacks to async/await.

function getFirstUser(callback) {
return getUsers().then(function(users) {
return callback(null, users[0].name);
}).catch(function(err) {
return callback(err);
});
}
function getFirstUser(callback) {
return getUsers().then(function(users) {
return users[0].name;
}).nodeify(callback);
}
function callbackToPromise(method, ...args) {
return new Promise(function(resolve, reject) {
return method(...args, function(err, result) {
return err ? reject(err) : resolve(result);
});
});
}
async function getFirstUser() {
let users = await callbackToPromise(getUsers);
return users[0].name;
}

Pitfall 4: Gotta remember to handle errors

The age-old problem with promises is also a problem with async/await: you need to remember to catch errors, or there’s the chance they could get lost in the ether.

myApp.endpoint('GET', '/api/firstUser', async function(req, res) {
let firstUser = await getFirstUser();
res.json(firstUser)
});
myApp.endpoint('GET', '/api/firstUser', function(req, res) {
return getFirstUser().then(function(firstUser) {
res.json(firstUser)
});
});
myApp.registerEndpoint('GET', '/api/firstUser', async function(req, res) {
try {
let firstUser = await getFirstUser();
res.json(firstUser)
} catch (err) {
console.error(err);
res.status(500);
}
});

--

--

I write code, sometimes my code works. daniel@bluesuncorp.co.uk

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store