But I'm worried that I might be missing something fundamental here.
Nope, you're not, that's exactly what you want to do, assuming this.doSomething(data)
is asynchronous (and if it's an ajax call, one hopes it is async) and that it returns a promise (which all functions defined with the async
keyword do implicitly). (If it's not asynchronous, you don't need the await
, although it's allowed.)
You're probably being a bit confused (understandably) by the fact that things are going through a transition. Until recently, the overwhelming convention in Node APIs (the built-in ones and ones provided by third-party modules) was to use the "Node callback" pattern, which is that a function that will do asynchronous work expects its last argument to be a callback, which it will call with a first argument indicating success/failure (null
= success, anything else is an error object) with subsequent arguments providing the result. (Your second example assumes doSomething
is one of these, instead of being a function that returns a promise.)
Example: fs.readFile
, which you use like this:
fs.readFile("/some/file", "utf-8", function(err, data) {
if (err) {
// ...handle the fact an error occurred..
return;
}
// ...use the data...
});
This style quickly leads to callback hell, though, which is one of the reasons promises (aka "futures") were invented.
But a lot of Node APIs still use the old pattern.
If you need to use "Node callback"-pattern functions, you can use util.promisify
to create promise-enabled versions of them. For instance, say you need to use fs.readFile
, which uses the Node callback pattern. You can get a promise version like this:
const readFilePromise = util.promisify(fs.readFile);
...and then use it with async
/await
syntax (or use the promise directly via then
):
const data = await readFilePromise("/some/file", "utf-8");
There's also an npm
module called promisify
that can provide a promise-ified version of an entire API. (There's probably more than one.)
Just because promises and async
/await
replace the old Node callback style in most cases doesn't mean callbacks don't still have a place: A Promise can only be settled once. They're for one-off things. So callbacks still have a place, such as with the on
method of EventEmitters, like a readable stream:
fs.createReadStream("/some/file", "utf-8")
.on("data", chunk => {
// ...do something with the chunk of data...
})
.on("end", () => {
// ...do something with the fact the end of the stream was reached...
});
Since data
will fire multiple times, it makes sense to use a callback for it; a promise wouldn't apply.
Also note that you can only use await
in an async
function. Consequently, you may find yourself getting into the habit of a "main" module structure that looks something like this:
// ...Setup (`require` calls, `import` once it's supported, etc.)...
(async () => {
// Code that can use `await `here...
})().catch(err => {
// Handle the fact an error/promise rejection occurred in the top level of your code
});
You can leave the catch
off if you want your script to terminate on an unhandled error/rejection. (Node doesn't do that yet, but it will once unhandled rejection detection matures.)
Alternately, if you want to use a promise-enabled function in a non-async
function, just use then
and catch
:
this.doSomething()
.then(result => {
// Use result
})
.catch(err => {
// Handle error
});
Note: async
/await
is directly supported in Node 7.x and above. Be sure your target production environment supports Node 7.x or above if your'e going to use async
/await
. If not, you could transpile your code using something like Babel and then use the transpiled result on the older version of Node.