| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,15 +9,15 @@ offloading operations to the system kernel whenever possible. | |||
| 9 | 9 | Since most modern kernels are multi-threaded, they can handle multiple | |
| 10 | 10 | operations executing in the background. When one of these operations | |
| 11 | 11 | completes, the kernel tells Node.js so that the appropriate callback | |
| 12 | - may added to the `poll` queue to eventually be executed. We'll explain | ||
| 12 | + may added to the **poll** queue to eventually be executed. We'll explain | ||
| 13 | 13 | this in further detail later in this topic. | |
| 14 | 14 | ||
| 15 | 15 | ## Event Loop Explained | |
| 16 | 16 | ||
| 17 | 17 | When Node.js starts, it initializes the event loop, processes the | |
| 18 | 18 | provided input script (or drops into the REPL, which is not covered in | |
| 19 | 19 | this document) which may make async API calls, schedule timers, or call | |
| 20 | - `process.nextTick()`, then begins processing the event loop. | ||
| 20 | + `process.nextTick()`, then begins processing the event loop. | ||
| 21 | 21 | ||
| 22 | 22 | The following diagram shows a simplified overview of the event loop's | |
| 23 | 23 | order of operations. | |
@@ -47,36 +47,36 @@ Each phase has a FIFO queue of callbacks to execute. While each phase is | |||
| 47 | 47 | special in its own way, generally, when the event loop enters a given | |
| 48 | 48 | phase, it will perform any operations specific to that phase, then | |
| 49 | 49 | execute callbacks in that phase's queue until the queue has been | |
| 50 | - exhausted or the maximum number of callbacks have executed. When the | ||
| 50 | + exhausted or the maximum number of callbacks has executed. When the | ||
| 51 | 51 | queue has been exhausted or the callback limit is reached, the event | |
| 52 | 52 | loop will move to the next phase, and so on. | |
| 53 | 53 | ||
| 54 | 54 | Since any of these operations may schedule _more_ operations and new | |
| 55 | - events processed in the `poll` phase are queued by the kernel, poll | ||
| 55 | + events processed in the **poll** phase are queued by the kernel, poll | ||
| 56 | 56 | events can be queued while polling events are being processed. As a | |
| 57 | 57 | result, long running callbacks can allow the poll phase to run much | |
| 58 | - longer than a timer's threshold. See the [`timers`](#timers) and | ||
| 59 | - [`poll`](#poll) sections for more details. | ||
| 58 | + longer than a timer's threshold. See the [**timers**](#timers) and | ||
| 59 | + [**poll**](#poll) sections for more details. | ||
| 60 | 60 | ||
| 61 | 61 | _**NOTE:** There is a slight discrepancy between the Windows and the | |
| 62 | 62 | Unix/Linux implementation, but that's not important for this | |
| 63 | 63 | demonstration. The most important parts are here. There are actually | |
| 64 | 64 | seven or eight steps, but the ones we care about — ones that Node.js | |
| 65 | - actually uses are those above._ | ||
| 65 | + actually uses - are those above._ | ||
| 66 | 66 | ||
| 67 | 67 | ||
| 68 | - ## Phases Overview: | ||
| 68 | + ## Phases Overview | ||
| 69 | 69 | ||
| 70 | - * `timers`: this phase executes callbacks scheduled by `setTimeout()` | ||
| 70 | + * **timers**: this phase executes callbacks scheduled by `setTimeout()` | ||
| 71 | 71 | and `setInterval()`. | |
| 72 | - * `I/O callbacks`: most types of callback except timers, setImmedate, close | ||
| 73 | - * `idle, prepare`: only used internally | ||
| 74 | - * `poll`: retrieve new I/O events; node will block here when appropriate | ||
| 75 | - * `check`: setImmediate callbacks are invoked here | ||
| 76 | - * `close callbacks`: e.g socket.on('close', ...) | ||
| 72 | + * **I/O callbacks**: most types of callback except timers, `setImmedate()`, close | ||
| 73 | + * **idle, prepare**: only used internally | ||
| 74 | + * **poll**: retrieve new I/O events; node will block here when appropriate | ||
| 75 | + * **check**: `setImmediate()` callbacks are invoked here | ||
| 76 | + * **close callbacks**: e.g socket.on('close', ...) | ||
| 77 | 77 | ||
| 78 | 78 | Between each run of the event loop, Node.js checks if it is waiting for | |
| 79 | - any asynchronous I/O or timer and it shuts down cleanly if there are not | ||
| 79 | + any asynchronous I/O or timers and shuts down cleanly if there are not | ||
| 80 | 80 | any. | |
| 81 | 81 | ||
| 82 | 82 | ## Phases in Detail | |
@@ -90,7 +90,7 @@ scheduled after the specified amount of time has passed; however, | |||
| 90 | 90 | Operating System scheduling or the running of other callbacks may delay | |
| 91 | 91 | them. | |
| 92 | 92 | ||
| 93 | - _**Note**: Technically, the [`poll` phase](#poll) controls when timers | ||
| 93 | + _**Note**: Technically, the [**poll** phase](#poll) controls when timers | ||
| 94 | 94 | are executed._ | |
| 95 | 95 | ||
| 96 | 96 | For example, say you schedule a timeout to execute after a 100 ms | |
@@ -102,10 +102,8 @@ takes 95 ms: | |||
| 102 | 102 | var fs = require('fs'); | |
| 103 | 103 | ||
| 104 | 104 | function someAsyncOperation (callback) { | |
| 105 | - | ||
| 106 | - // let's assume this takes 95ms to complete | ||
| 105 | + // Assume this takes 95ms to complete | ||
| 107 | 106 | fs.readFile('/path/to/file', callback); | |
| 108 | - | ||
| 109 | 107 | } | |
| 110 | 108 | ||
| 111 | 109 | var timeoutScheduled = Date.now(); | |
@@ -131,78 +129,77 @@ someAsyncOperation(function () { | |||
| 131 | 129 | }); | |
| 132 | 130 | ``` | |
| 133 | 131 | ||
| 134 | - When the event loop enters the `poll` phase, it has an empty queue | ||
| 135 | - (`fs.readFile()` has not completed) so it will wait for the number of ms | ||
| 132 | + When the event loop enters the **poll** phase, it has an empty queue | ||
| 133 | + (`fs.readFile()` has not completed), so it will wait for the number of ms | ||
| 136 | 134 | remaining until the soonest timer's threshold is reached. While it is | |
| 137 | 135 | waiting 95 ms pass, `fs.readFile()` finishes reading the file and its | |
| 138 | - callback which takes 10 ms to complete is added to the `poll` queue and | ||
| 136 | + callback which takes 10 ms to complete is added to the **poll** queue and | ||
| 139 | 137 | executed. When the callback finishes, there are no more callbacks in the | |
| 140 | 138 | queue, so the event loop will see that the threshold of the soonest | |
| 141 | - timer has been reached then wrap back to the `timers` phase to execute | ||
| 139 | + timer has been reached then wrap back to the **timers** phase to execute | ||
| 142 | 140 | the timer's callback. In this example, you will see that the total delay | |
| 143 | - between the timer being scheduled and its callback being executed will | ||
| 141 | + between the timer being scheduled and its callback being executed will | ||
| 144 | 142 | be 105ms. | |
| 145 | 143 | ||
| 146 | - Note: To prevent the `poll` phase from starving the event loop, libuv | ||
| 147 | - also has a hard maximum (system dependent) before it stops `poll`ing for | ||
| 144 | + Note: To prevent the **poll** phase from starving the event loop, libuv | ||
| 145 | + also has a hard maximum (system dependent) before it stops polling for | ||
| 148 | 146 | more events. | |
| 149 | 147 | ||
| 150 | - ### I/O callbacks: | ||
| 148 | + ### I/O callbacks | ||
| 151 | 149 | ||
| 152 | 150 | This phase executes callbacks for some system operations such as types | |
| 153 | 151 | of TCP errors. For example if a TCP socket receives `ECONNREFUSED` when | |
| 154 | 152 | attempting to connect, some \*nix systems want to wait to report the | |
| 155 | - error. This will be queued to execute in the `I/O callbacks` phase. | ||
| 156 | - | ||
| 157 | - ### poll: | ||
| 153 | + error. This will be queued to execute in the **I/O callbacks** phase. | ||
| 158 | 154 | ||
| 159 | - The poll phase has two main functions: | ||
| 155 | + ### poll | ||
| 160 | 156 | ||
| 161 | - 1. Executing scripts for timers who's threshold has elapsed, then | ||
| 162 | - 2. Processing events in the `poll` queue. | ||
| 157 | + The **poll** phase has two main functions: | ||
| 163 | 158 | ||
| 159 | + 1. Executing scripts for timers whose threshold has elapsed, then | ||
| 160 | + 2. Processing events in the **poll** queue. | ||
| 164 | 161 | ||
| 165 | - When the event loop enters the `poll` phase _and there are no timers | ||
| 162 | + When the event loop enters the **poll** phase _and there are no timers | ||
| 166 | 163 | scheduled_, one of two things will happen: | |
| 167 | 164 | ||
| 168 | - * _If the `poll` queue **is not empty**_, the event loop will iterate | ||
| 169 | - through its queue of callbacks executing them synchronously until | ||
| 170 | - either the queue has been exhausted, or the system-dependent hard limit | ||
| 165 | + * _If the **poll** queue **is not empty**_, the event loop will iterate | ||
| 166 | + through its queue of callbacks executing them synchronously until | ||
| 167 | + either the queue has been exhausted, or the system-dependent hard limit | ||
| 171 | 168 | is reached. | |
| 172 | 169 | ||
| 173 | - * _If the `poll` queue **is empty**_, one of two more things will | ||
| 170 | + * _If the **poll** queue **is empty**_, one of two more things will | ||
| 174 | 171 | happen: | |
| 175 | 172 | * If scripts have been scheduled by `setImmediate()`, the event loop | |
| 176 | - will end the `poll` phase and continue to the `check` phase to | ||
| 173 | + will end the **poll** phase and continue to the **check** phase to | ||
| 177 | 174 | execute those scheduled scripts. | |
| 178 | 175 | ||
| 179 | 176 | * If scripts **have not** been scheduled by `setImmediate()`, the | |
| 180 | 177 | event loop will wait for callbacks to be added to the queue, then | |
| 181 | - execute it immediately. | ||
| 178 | + execute them immediately. | ||
| 182 | 179 | ||
| 183 | - Once the `poll` queue is empty the event loop will check for timers | ||
| 180 | + Once the **poll** queue is empty the event loop will check for timers | ||
| 184 | 181 | _whose time thresholds have been reached_. If one or more timers are | |
| 185 | - ready, the event loop will wrap back to the timers phase to execute | ||
| 182 | + ready, the event loop will wrap back to the **timers** phase to execute | ||
| 186 | 183 | those timers' callbacks. | |
| 187 | 184 | ||
| 188 | - ### `check`: | ||
| 185 | + ### check | ||
| 189 | 186 | ||
| 190 | - This phase allows a person to execute callbacks immediately after the | ||
| 191 | - `poll` phase has completed. If the `poll` phase becomes idle and | ||
| 192 | - scripts have been queued with `setImmediate()`, the event loop may | ||
| 193 | - continue to the `check` phase rather than waiting. | ||
| 187 | + This phase allows a person to execute callbacks immediately after the | ||
| 188 | + **poll** phase has completed. If the **poll** phase becomes idle and | ||
| 189 | + scripts have been queued with `setImmediate()`, the event loop may | ||
| 190 | + continue to the **check** phase rather than waiting. | ||
| 194 | 191 | ||
| 195 | 192 | `setImmediate()` is actually a special timer that runs in a separate | |
| 196 | 193 | phase of the event loop. It uses a libuv API that schedules callbacks to | |
| 197 | - execute after the `poll` phase has completed. | ||
| 194 | + execute after the **poll** phase has completed. | ||
| 198 | 195 | ||
| 199 | 196 | Generally, as the code is executed, the event loop will eventually hit | |
| 200 | - the `poll` phase where it will wait for an incoming connection, request, | ||
| 201 | - etc. However, after a callback has been scheduled with `setImmediate()`, | ||
| 202 | - then the `poll` phase becomes idle, it will end and continue to the | ||
| 203 | - `check` phase rather than waiting for `poll` events. | ||
| 197 | + the **poll** phase where it will wait for an incoming connection, request, | ||
| 198 | + etc. However, if a callback has been scheduled with `setImmediate()` | ||
| 199 | + and the **poll** phase becomes idle, it will end and continue to the | ||
| 200 | + **check** phase rather than waiting for **poll** events. | ||
| 204 | 201 | ||
| 205 | - ### `close callbacks`: | ||
| 202 | + ### close callbacks | ||
| 206 | 203 | ||
| 207 | 204 | If a socket or handle is closed abruptly (e.g. `socket.destroy()`), the | |
| 208 | 205 | `'close'` event will be emitted in this phase. Otherwise it will be | |
@@ -214,12 +211,12 @@ emitted via `process.nextTick()`. | |||
| 214 | 211 | ways depending on when they are called. | |
| 215 | 212 | ||
| 216 | 213 | * `setImmediate()` is designed to execute a script once the current | |
| 217 | - `poll` phase completes. | ||
| 218 | - * `setTimeout()` schedules a script to be run | ||
| 219 | - after a minimum threshold in ms has elapsed. | ||
| 214 | + **poll** phase completes. | ||
| 215 | + * `setTimeout()` schedules a script to be run after a minimum threshold | ||
| 216 | + in ms has elapsed. | ||
| 220 | 217 | ||
| 221 | 218 | The order in which the timers are executed will vary depending on the | |
| 222 | - context in which they are called. If both are called from within the | ||
| 219 | + context in which they are called. If both are called from within the | ||
| 223 | 220 | main module, then timing will be bound by the performance of the process | |
| 224 | 221 | (which can be impacted by other applications running on the machine). | |
| 225 | 222 | ||
@@ -248,7 +245,6 @@ setImmediate(function immediate () { | |||
| 248 | 245 | immediate | |
| 249 | 246 | timeout | |
| 250 | 247 | ||
| 251 | - | ||
| 252 | 248 | However, if you move the two calls within an I/O cycle, the immediate | |
| 253 | 249 | callback is always executed first: | |
| 254 | 250 | ||
@@ -278,22 +274,22 @@ The main advantage to using `setImmediate()` over `setTimeout()` is | |||
| 278 | 274 | `setImmediate()` will always be executed before any timers if scheduled | |
| 279 | 275 | within an I/O cycle, independently of how many timers are present. | |
| 280 | 276 | ||
| 281 | - ## `process.nextTick()`: | ||
| 277 | + ## `process.nextTick()` | ||
| 282 | 278 | ||
| 283 | 279 | ### Understanding `process.nextTick()` | |
| 284 | 280 | ||
| 285 | 281 | You may have noticed that `process.nextTick()` was not displayed in the | |
| 286 | - diagram, even though its a part of the asynchronous API. This is because | ||
| 282 | + diagram, even though it's a part of the asynchronous API. This is because | ||
| 287 | 283 | `process.nextTick()` is not technically part of the event loop. Instead, | |
| 288 | - the nextTickQueue will be processed after the current operation | ||
| 289 | - completes, regardless of the current `phase` of the event loop. | ||
| 284 | + the `nextTickQueue` will be processed after the current operation | ||
| 285 | + completes, regardless of the current phase of the event loop. | ||
| 290 | 286 | ||
| 291 | 287 | Looking back at our diagram, any time you call `process.nextTick()` in a | |
| 292 | 288 | given phase, all callbacks passed to `process.nextTick()` will be | |
| 293 | 289 | resolved before the event loop continues. This can create some bad | |
| 294 | 290 | situations because **it allows you to "starve" your I/O by making | |
| 295 | - recursive `process.nextTick()` calls.** which prevents the event loop | ||
| 296 | - from reaching the `poll` phase. | ||
| 291 | + recursive `process.nextTick()` calls**, which prevents the event loop | ||
| 292 | + from reaching the **poll** phase. | ||
| 297 | 293 | ||
| 298 | 294 | ### Why would that be allowed? | |
| 299 | 295 | ||
@@ -319,9 +315,9 @@ What we're doing is passing an error back to the user but only *after* | |||
| 319 | 315 | we have allowed the rest of the user's code to execute. By using | |
| 320 | 316 | `process.nextTick()` we guarantee that `apiCall()` always runs its | |
| 321 | 317 | callback *after* the rest of the user's code and *before* the event loop | |
| 322 | - is allowed to proceed. To acheive this, the JS call stack is allowed to | ||
| 318 | + is allowed to proceed. To achieve this, the JS call stack is allowed to | ||
| 323 | 319 | unwind then immediately execute the provided callback which allows a | |
| 324 | - person to make recursive calls to nextTick without reaching a | ||
| 320 | + person to make recursive calls to `process.nextTick()` without reaching a | ||
| 325 | 321 | `RangeError: Maximum call stack size exceeded from v8`. | |
| 326 | 322 | ||
| 327 | 323 | This philosophy can lead to some potentially problematic situations. | |
@@ -343,21 +339,33 @@ var bar = 1; | |||
| 343 | 339 | ``` | |
| 344 | 340 | ||
| 345 | 341 | The user defines `someAsyncApiCall()` to have an asynchronous signature, | |
| 346 | - actually operates synchronously. When it is called, the callback | ||
| 347 | - provided to `someAsyncApiCall ()` is called in the same phase of the | ||
| 342 | + but it actually operates synchronously. When it is called, the callback | ||
| 343 | + provided to `someAsyncApiCall()` is called in the same phase of the | ||
| 348 | 344 | event loop because `someAsyncApiCall()` doesn't actually do anything | |
| 349 | - asynchronously. As a result, the callback tries to reference `bar` but | ||
| 350 | - it may not have that variable in scope yet because the script has not | ||
| 345 | + asynchronously. As a result, the callback tries to reference `bar` even | ||
| 346 | + though it may not have that variable in scope yet, because the script has not | ||
| 351 | 347 | been able to run to completion. | |
| 352 | 348 | ||
| 353 | - By placing it in a `process.nextTick()`, the script still has the | ||
| 349 | + By placing the callback in a `process.nextTick()`, the script still has the | ||
| 354 | 350 | ability to run to completion, allowing all the variables, functions, | |
| 355 | - etc., to be initialized prior to the callback being called. It also has | ||
| 351 | + etc., to be initialized prior to the callback being called. It also has | ||
| 356 | 352 | the advantage of not allowing the event loop to continue. It may be | |
| 357 | - useful that the user be alerted to an error before the event loop is | ||
| 358 | - allowed to continue. | ||
| 353 | + useful for the user to be alerted to an error before the event loop is | ||
| 354 | + allowed to continue. Here is the previous example using `process.nextTick()`: | ||
| 355 | + | ||
| 356 | + ```js | ||
| 357 | + function someAsyncApiCall (callback) { | ||
| 358 | + process.nextTick(callback); | ||
| 359 | + }; | ||
| 360 | + | ||
| 361 | + someAsyncApiCall(() => { | ||
| 362 | + console.log('bar', bar); // 1 | ||
| 363 | + }); | ||
| 364 | + | ||
| 365 | + var bar = 1; | ||
| 366 | + ``` | ||
| 359 | 367 | ||
| 360 | - A real world example in node would be: | ||
| 368 | + Here's another real world example: | ||
| 361 | 369 | ||
| 362 | 370 | ```js | |
| 363 | 371 | const server = net.createServer(() => {}).listen(8080); | |
@@ -367,10 +375,10 @@ server.on('listening', () => {}); | |||
| 367 | 375 | ||
| 368 | 376 | When only a port is passed the port is bound immediately. So the | |
| 369 | 377 | `'listening'` callback could be called immediately. Problem is that the | |
| 370 | - `.on('listening')` will not have been set by that time. | ||
| 378 | + `.on('listening')` will not have been set by that time. | ||
| 371 | 379 | ||
| 372 | 380 | To get around this the `'listening'` event is queued in a `nextTick()` | |
| 373 | - to allow the script to run to completion. Which allows the user to set | ||
| 381 | + to allow the script to run to completion. Which allows the user to set | ||
| 374 | 382 | any event handlers they want. | |
| 375 | 383 | ||
| 376 | 384 | ## `process.nextTick()` vs `setImmediate()` | |
@@ -389,7 +397,7 @@ percentage of the packages on npm. Every day more new modules are being | |||
| 389 | 397 | added, which mean every day we wait, more potential breakages occur. | |
| 390 | 398 | While they are confusing, the names themselves won't change. | |
| 391 | 399 | ||
| 392 | - *We recommend developers use `setImmediate()` in all cases because its | ||
| 400 | + *We recommend developers use `setImmediate()` in all cases because it's | ||
| 393 | 401 | easier to reason about (and it leads to code that's compatible with a | |
| 394 | 402 | wider variety of environments, like browser JS.)* | |
| 395 | 403 | ||
@@ -413,11 +421,11 @@ server.listen(8080); | |||
| 413 | 421 | server.on('listening', function() { }); | |
| 414 | 422 | ``` | |
| 415 | 423 | ||
| 416 | - Say that listen() is run at the beginning of the event loop, but the | ||
| 424 | + Say that `listen()` is run at the beginning of the event loop, but the | ||
| 417 | 425 | listening callback is placed in a `setImmediate()`. Now, unless a | |
| 418 | - hostname is passed binding to the port will happen immediately. Now for | ||
| 419 | - the event loop to proceed it must hit the `poll` phase, which means | ||
| 420 | - there is a non-zero chance that a connection could have been received | ||
| 426 | + hostname is passed binding to the port will happen immediately. Now for | ||
| 427 | + the event loop to proceed it must hit the **poll** phase, which means | ||
| 428 | + there is a non-zero chance that a connection could have been received | ||
| 421 | 429 | allowing the connection event to be fired before the listening event. | |
| 422 | 430 | ||
| 423 | 431 | Another example is running a function constructor that was to, say, | |
| Back | FazBrowse Home | New Git URL |
0 commit comments