async/await patterns
// Sources:
// - http://exploringjs.com/es2016-es2017/ch_async-functions.html
// - http://thecodebarbarian.com/common-async-await-design-patterns-in-node.js.html
// - http://thecodebarbarian.com/80-20-guide-to-async-await-in-node.js.html
// There is one small improvement that we still can make:
// This async function is slightly inefficient – it first unwraps the result
// of Promise.all() via await, before wrapping it again via return. Given that
//return doesn’t wrap Promises, we can return the result of Promise.all()
// directly:
async function downloadContent(urls) {
const promiseArray = urls.map(
url => httpGet(url));
return Promise.all(promiseArray);
}
// convert forEach() into a for-of loop (sequential):
async function logContent(urls) {
for (const url of urls) {
const content = await httpGet(url);
console.log(content);
}
}
//parallel
async function logContent(urls) {
await Promise.all(urls.map(
async url => {
const content = await httpGet(url);
console.log(content);
}));
}
//------------
//Retrying failed requests
const superagent = require('superagent');
const NUM_RETRIES = 3;
test();
async function test() {
let i;
for (i = 0; i < NUM_RETRIES; ++i) {
try {
await superagent.get('http://google.com/this-throws-an-error');
break;
} catch(err) {}
}
console.log(i); // 3
}
//----------
//Processing a MongoDB Cursor
const mongodb = require('mongodb');
test();
async function test() {
const db = await mongodb.MongoClient.connect('mongodb://localhost:27017/test');
await db.collection('Movies').drop();
await db.collection('Movies').insertMany([
{ name: 'Enter the Dragon' },
{ name: 'Ip Man' },
{ name: 'Kickboxer' }
]);
// Don't `await`, instead get a cursor
const cursor = db.collection('Movies').find();
// Use `next()` and `await` to exhaust the cursor
for (let doc = await cursor.next(); doc != null; doc = await cursor.next()) {
console.log(doc.name);
}
}
//proposal for async iterators, wont work now
const cursor = db.collection('Movies').find().map(value => ({
value,
done: !value
}));
for await (const doc of cursor) {
console.log(doc.name);
}
//------------
//Multiple Requests in Parallel
const bcrypt = require('bcrypt');
const NUM_SALT_ROUNDS = 8;
test();
async function test() {
const pws = ['password', 'password1', 'passw0rd'];
// `promises` is an array of promises, because `bcrypt.hash()` returns a
// promise if no callback is supplied.
const promises = pws.map(pw => bcrypt.hash(pw, NUM_SALT_ROUNDS));
/**
* Prints hashed passwords, for example:
* [ '$2a$08$nUmCaLsQ9rUaGHIiQgFpAOkE2QPrn1Pyx02s4s8HC2zlh7E.o9wxC',
* '$2a$08$wdktZmCtsGrorU1mFWvJIOx3A0fbT7yJktRsRfNXa9HLGHOZ8GRjS',
* '$2a$08$VCdMy8NSwC8r9ip8eKI1QuBd9wSxPnZoZBw8b1QskK77tL2gxrUk.' ]
*/
console.log(await Promise.all(promises));
}
////Multiple Requests in Parallel with Promise.race
/**
* Prints below:
* waited 250
* resolved to 250
* waited 500
* waited 1000
*/
test();
async function test() {
const promises = [250, 500, 1000].map(ms => wait(ms));
console.log('resolved to', await Promise.race(promises));
}
async function wait(ms) {
await new Promise(resolve => setTimeout(() => resolve(), ms));
console.log('waited', ms);
return ms;
}
//Express error handling with async/await
const express = require('express');
const app = express();
app.get('/', safeHandler(handler));
app.listen(3000);
function safeHandler(handler) {
return function(req, res) {
handler(req, res).catch(error => res.status(500).send(error.message));
};
}
async function handler(req, res) {
await new Promise((resolve, reject) => reject(new Error('Hang!')));
res.send('Hello, World!');
}