-
Notifications
You must be signed in to change notification settings - Fork 30.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
doc: topic blocking vs non-blocking #5326
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
# Overview of Blocking vs Non-Blocking | ||
|
||
This overview covers the difference between blocking and non-blocking calls in | ||
Node.js. This overview will refer to the event loop and libuv but no prior | ||
knowledge of those topics is required. Readers are assumed to have a basic | ||
understanding of the JavaScript language and Node.js callback pattern. | ||
|
||
> In this overview, "I/O" refers primarily to interaction with the system's disk | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. minor nit: avoid duplciation of "This overview". (am no native speaker) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 |
||
and network supported by [libuv](http://libuv.org/). | ||
|
||
|
||
## Blocking | ||
|
||
Blocking is when the execution of additional JavaScript in the Node.js process | ||
must wait until an I/O operation completes. Blocking may occur when using any of | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: it's not just limited to i/o operations There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be more appropriate to just take I/O out of the sentence or is there a better way to explain this? My goal is to distinguish that the process is waiting not just because JavaScript is executing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Leaving the I/O in there is fine since that's the most likely case. It may just be worthwhile indicating that non-I/O operations can cause it to bog down as well. crypto operations, for instance. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure you consider things like waiting for synchronization ( a lock for instance) I/O. |
||
the synchronous I/O methods in the Node.js standard library that use libuv. Use | ||
of blocking methods prevents the event loop from doing additional work while | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. technically any native module can do any sort of blocking - it doesn't have to be through libuv but I guess that absolute correctness isn't a goal here. |
||
waiting for I/O to complete. | ||
|
||
In Node.js, JavaScript that exhibits poor performance due to being CPU intensive | ||
rather than I/O bound, isn't typically referred to as blocking. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are two notions of what "blocking" is - one is "blocking the event loop" and one is "blocked as in the waiting thread state". When people use "blocking" in node sometimes they mean one and sometimes they mean the other. |
||
|
||
All of the I/O methods in the Node.js standard libraries provide asynchronous | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. standard library, right? Did you mean core modules? |
||
versions, which are non-blocking, and accept callback functions. Some methods | ||
also implement blocking methods, which usually have names that end with `Sync`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Some methods also implement" - the methods don't implement other methods. Maybe "Some methods also have blocking counterparts implemented"? |
||
|
||
|
||
## Comparing Code | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we |
||
|
||
Using the File System module as an example, this is a synchronous file read: | ||
|
||
```js | ||
const fs = require('fs'); | ||
const data = fs.readFileSync('/file.md'); // blocks here until file is read | ||
``` | ||
|
||
And here is an equivalent asynchronous example: | ||
|
||
```js | ||
const fs = require('fs'); | ||
fs.readFile('/file.md', (err, data) => { | ||
if (err) throw err; | ||
}); | ||
``` | ||
|
||
The first example appears simpler but it has the disadvantage of the second line | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
comparative degree used. Shouldn't this be
or simply
|
||
blocking the execution of any additional JavaScript until the entire file is | ||
read. Note that in the synchronous version if an error is thrown it will need to | ||
be caught or the process will crash. In the asynchronous version, it is up to | ||
the author to decide whether an error should throw as shown. | ||
|
||
Let's expand our example a little bit: | ||
|
||
```js | ||
const fs = require('fs'); | ||
const data = fs.readFileSync('/file.md'); // blocks here until file is read | ||
console.log(data); | ||
moreWork(); // will run after console.log | ||
``` | ||
|
||
And here is a similar, but not equivalent asynchronous example: | ||
|
||
```js | ||
const fs = require('fs'); | ||
fs.readFile('/file.md', (err, data) => { | ||
if (err) throw err; | ||
console.log(data); | ||
}); | ||
moreWork(); // will run before console.log | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. preferably do |
||
``` | ||
|
||
In the first example above, `console.log` will be called before `moreWork`. In | ||
the second example `readFile` is non-blocking so JavaScript execution can | ||
continue and `moreWork` will be called first. The ability to run `moreWork` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question - are we always guaranteed that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand that to be the case except for situations where there is an immediate err callback, like when passing |
||
without waiting for the file read to complete is a key design choice that allows | ||
for higher throughout. | ||
|
||
|
||
## Concurrency and Throughput | ||
|
||
The Node.js event loop is single threaded, so concurrency refers to the event | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it's worth phrasing as JavaScript execution since the event loop itself is not single threaded. |
||
loop's capacity to resume execution of a JavaScript callback function after | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically it doesn't resume execution - it initiates it. JavaScript always runs synchronously to completion. |
||
completing other work. Any code that is expected to process requests in a | ||
concurrent manner depends on the ability of the event loop to continue running | ||
as asynchronous I/O occurs. | ||
|
||
As an example, let's consider a case where each request to a web server takes | ||
50ms to complete and 45ms of that 50ms is database I/O that can be done | ||
asychronously. Choosing non-blocking asynchronous operations frees up that 45ms | ||
per request to handle other requests. This is an effective 90% difference in | ||
capacity just by choosing to use non-blocking methods instead of blocking | ||
methods. | ||
|
||
The event loop is different than models in many other languages where additional | ||
threads may be created to handle concurrent work. For an introduction to the | ||
event loop see [Overview of the Event Loop, Timers, and | ||
`process.nextTick()`](https://github.com/nodejs/node/pull/4936) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, PRs are WIP items. So quoting them is a not a good idea I think. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This shouldn't be merged with this like this, my issue is that there should be a reference to the other topic on nodejs.org but the PR hasn't even been merged into master yet. This was mentioned above when the PR was opened. |
||
|
||
|
||
## Dangers of Mixing Blocking and Non-Blocking Code | ||
|
||
There are some patterns that should be avoided when dealing with I/O. Let's look | ||
at an example: | ||
|
||
```js | ||
const fs = require('fs'); | ||
fs.readFile('/file.md', (err, data) => { | ||
if (err) throw err; | ||
console.log(data); | ||
}); | ||
fs.unlinkSync('/file.md'); | ||
``` | ||
|
||
In the above example, `unlinkSync` is likely to be run before `readFile`, which | ||
would delete `file.md` before it is actually read. A better way to write this | ||
that is completely non-blocking and guaranteed to execute in the correct order | ||
is: | ||
|
||
|
||
```js | ||
const fs = require('fs'); | ||
fs.readFile('/file.md', (err, data) => { | ||
if (err) throw err; | ||
console.log(data); | ||
fs.unlink('/file.md', err => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use parens consistently with arrow functions. |
||
if (err) throw err; | ||
}); | ||
}); | ||
``` | ||
|
||
The above places a non-blocking call to `unlink` within the callback of | ||
`readFile` which guarantees the correct order of operations. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you refer here always to |
||
|
||
|
||
## Additional Resources | ||
|
||
- [libuv](http://libuv.org/) | ||
- [Overview of the Event Loop, Timers, and | ||
`process.nextTick()`](https://github.com/nodejs/node/pull/4936) | ||
- [About Node.js](https://nodejs.org/en/about/) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to
highlight, blocking and non-blocking toblocking
andnon-blocking
. That way it draws the readers eyes.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, we usually reserve the code
highlighting
for code or literal values. It may be confusing. Perhaps just use bold?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. Might be enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jasnell +1