function doSomeAsyncStuff(done) {
getData(function(err, x) {
if (err) {
return done(err);
}
return getMoreData(x, function(err, y) {
if (err) {
return done(err);
}
return getMoreData(y, function(err, z) {
if (err) {
return done(err);
}
return done(null, z);
});
});
});
}
- Harder to read, harder to debug, especially for newbies
- Convention cb(err, result) not necessarily obvious
- Doesn't feel the same as reading linear and sequential code
- More prone to errors?
const async = require('async');
async.waterfall(
[
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
callback(null, 'three');
},
function(arg1, callback) {
callback(null, 'done');
},
],
function(err, result) {},
);
- Using an external module not standard JS features
- You need to read the documentation of that library
- This code will be transformed, harder to debug (stack trace?)
- Still doesn't feel like reading linear and sequential code
Example where you can't just sequentially .then()
or you'll create a closure and won't be able to access valueA
inside functionC(valueA, valueB)
// Not working
function executeAsyncTask() {
return functionA()
.then(valueA => functionB(valueA))
.then(valueB => functionC(valueA, valueB))
// valueA is undefined ⬆️
}).catch(err => console.log(err));
}
Better with the Promises "Christmas tree" 🎄?
function executeAsyncTask() {
return functionA().then(valueA => {
return functionB(valueA).then(valueB => {
return functionC(valueA, valueB);
});
}).catch(err => console.log(err));
}
Better with variables at the top?
function executeAsyncTask() {
let valueA;
return functionA()
.then(v => {
valueA = v;
return functionB(valueA);
})
.then(valueB => {
return functionC(valueA, valueB);
})
.catch(err => console.log(err));
}
Spec: https://tc39.github.io/ecmascript-asyncawait/
// async keyword before declaring an async function
async function getPageTitle(url) {
try {
// stop here until request is resolved
const response = await request(url);
// wait here until request has been resolved before returning the response
return response;
} catch (err) {
// catch if the request() is rejected or
// any errors are thrown
return err;
}
}
Browsers https://caniuse.com/#feat=async-functions Node http://node.green
With Babel you need to transform async to generators that will make it work on node
> 4.
For a new project I suggest babel-preset-env which compiles ES2015+ down to ES5 by automatically determining the Babel plugins and polyfills you need based on your targeted browser or runtime environments.
{
"presets": [
["@babel/env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"],
"node": "6.10"
}
}]
]
}
Every async
function works with an underlying Promise
and so it will always return a Promise
that can be consumed as you are used to.
async function getPage(id) {
return await request(`/pages/${id}`);
}
getPage(1)
.then(response => console.log(response))
.catch(err => console.log(err));
- Calling
await
inside a function that doesn't have the keywordasync
will throw an error - You can't call
await
at the top level at the moment in node and all the browsers except Chrome.
This will take 1000ms total
async function series() {
await wait(500);
await wait(500);
return 'done!';
}
This instead would take only 500ms total
async function parallel() {
const wait1 = wait(500);
const wait2 = wait(500);
await wait1;
await wait2;
return 'done!';
}
Promise.all[]
still works perfectly
let [resA, resB] = await Promise.all([wait(500), wait(500)])
See errors.test.js
Left them at the bottom since I haven't used them much but reading here and there, seems like using a library like https://github.com/tj/co you can use generators more or less in the same way as you use async/await. There should be no reason anymore to use this.
const co = require('co');
co(function *(){
// yield any promise
var result = yield Promise.resolve(true);
}).catch(onerror);