Reputation: 7098
I recently saw this kind of code in an asynchronous function:
async function foo() {
const readFile = util.promisify(fs.readFile);
const data = await readFile(file, 'utf8');
// .... other code
}
Is there any advantange of doing this over:
async function foo() {
const data = readFileSync(file, 'utf8');
// ... other code
}
?
In general I am interested in if there is any advantage over calling an asynchronous function followed by an immediate wait on the return, as opposed to calling the corresponding synchronous function without the wait.
And suppose this async function had already been wrapped say in a promise?
Note: some of the comments and answers refer to an earlier version of this code which was buggy and less clear about the intent.
Upvotes: 3
Views: 269
Reputation: 7098
So now that I think I understand the situation, let me try to explain my confusion.
I had thought V8 or nodejs maintained a list of runnable threads even if only one was ever running at a given time. Also I assued that that async functions created a new thread. Thus if that thread was blocked, no problem, because some other runabble thread would be swapped in.
From what people have posted, I now am given to believe that there is only one thread (at least visible to the programmer), so blocking anywhere (whether or not there are coroutines/async functions) blocks everywhere.
Upvotes: 0
Reputation: 2408
The answer to your very valid question is that the synchronous methods will block the thread, whereas the async methods will allow other JavaScript to run while the operation takes place, giving your application more room to scale.
For example, if you wrote an http server and only have one request coming in per minute and never simultaneously, it wouldn't matter much what you do. The thread is being blocked for mere milliseconds and that work is unavoidable with respect to the request that is relying on it. However, if you have 100 requests per minute coming through, and maybe some other work being done on some cron schedule in the same JS app, then using the asynchronous methods would allow those requests to continue to come through your server code (JS) while the filesystem work is being done behind the scenes (in C++, I believe, but you'd have to google that part). When the thread is blocked by a synchronous function, then all of those requests and events pile up in the event queue waiting for the JS to handle them, even though the JS event loop probably isn't busy with anything directly. It's only waiting. That's not good, so take advantage of the asynchronous nature of single-threaded JS as much as possible.
With respect to error handling, the async versions of those functions have an error object (or null
when successful) as the callback's first parameter, which is transferred to the .catch
method when you promisify it. This allows for natural and built-in error handling. You will observe with the *Sync
functions that there is no callback, and, if it errors, you will not receive that error in a helpful way (usually it crashes the app). Here are two examples to illustrate:
Unhelpful.js
const fs = require('fs')
const util = require('util')
const test = fs.readFileSync('./test.js') // spoiler alert: ./test.js doesn't exist
console.log('test:', test) // never runs, because the error thrown is not caught and the process exits :(
/*
Output:
fs.js:113
throw err;
^
Error: ENOENT: no such file or directory, open './test.js'
at Object.openSync (fs.js:434:3)
at Object.readFileSync (fs.js:339:35)
...etc...
*/
Better.js
const fs = require('fs')
const util = require('util')
const readFile = util.promisify(fs.readFile)
;(async function () {
// The catch handler actually returns the error object, so test will hold
// the value/result of the readFile operation whether it succeeds or fails
const test = await readFile('./test.js').catch(err => err instanceof Error ? err : new Error(err))
console.log('test:', test)
// helpful log, as a result:
// test: { [Error: ENOENT: no such file or directory, open './test.js'] errno: -2, code: 'ENOENT', syscall: 'open', path: './test.js' }
})()
Upvotes: 5