diff --git a/doc/guides/timers-in-node.md b/doc/guides/timers-in-node.md new file mode 100644 index 00000000000000..3be7069e4389ae --- /dev/null +++ b/doc/guides/timers-in-node.md @@ -0,0 +1,192 @@ +--- +title: Timers in Node.js +layout: docs.hbs +--- + +# Timers in Node.js and beyond + +The Timers module in Node.js contains functions that execute code after a set +period of time. Timers do not need to be imported via `require()`, since +all the methods are available globally to emulate the browser JavaScript API. +To fully understand when timer functions will be executed, it's a good idea to +read up on the the Node.js +[Event Loop](../topics/the-event-loop-timers-and-nexttick). + +## Controlling the Time Continuum with Node.js + +The Node.js API provides several ways of scheduling code to execute at +some point after the present moment. The functions below may seem familiar, +since they are available in most browsers, but Node.js actually provides +its own implementation of these methods. Timers integrate very closely +with the system, and despite the fact that the API mirrors the browser +API, there are some differences in implementation. + +### "When I say so" Execution ~ *`setTimeout()`* + +`setTimeout()` can be used to schedule code execution after a designated +amount of milliseconds. This function is similar to +[`window.setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout) +from the browser JavaScript API, however a string of code cannot be passed +to be executed. + +`setTimeout()` accepts a function to execute as its first argument and the +millisecond delay defined as a number as the second argument. Additional +arguments may also be included and these will be passed on to the function. Here +is an example of that: + +```js +function myFunc (arg) { + console.log('arg was => ' + arg); +} + +setTimeout(myFunc, 1500, 'funky'); +``` + +The above function `myFunc()` will execute as close to 1500 +milliseconds (or 1.5 seconds) as possible due to the call of `setTimeout()`. + +The timeout interval that is set cannot be relied upon to execute after +that *exact* number of milliseconds. This is because other executing code that +blocks or holds onto the event loop will push the execution of the timeout +back. The *only* guarantee is that the timeout will not execute *sooner* than +the declared timeout interval. + +`setTimeout()` returns a `Timeout` object that can be used to reference the +timeout that was set. This returned object can be used to cancel the timeout ( +see `clearTimeout()` below) as well as change the execution behavior (see +`unref()` below). + +### "Right after this" Execution ~ *`setImmediate()`* + +`setImmediate()` will execute code at the end of the current event loop cycle. +This code will execute *after* any I/O operations in the current event loop and +*before* any timers scheduled for the next event loop. This code execution +could be thought of as happening "right after this", meaning any code following +the `setImmediate()` function call will execute before the `setImmediate()` +function argument. + +The first argument to `setImmediate()` will be the function to execute. Any +subsequent arguments will be passed to the function when it is executed. +Here's an example: + +```js +console.log('before immediate'); + +setImmediate((arg) => { + console.log(`executing immediate: ${arg}`); +}, 'so immediate'); + +console.log('after immediate'); +``` + +The above function passed to `setImmediate()` will execute after all runnable +code has executed, and the console output will be: + +```shell +before immediate +after immediate +executing immediate: so immediate +``` + +`setImmediate()` returns and `Immediate` object, which can be used to cancel +the scheduled immediate (see `clearImmediate()` below). + +Note: Don't get `setImmediate()` confused with `process.nextTick()`. There are +some major ways they differ. The first is that `process.nextTick()` will run +*before* any `Immediate`s that are set as well as before any scheduled I/O. +The second is that `process.nextTick()` is non-clearable, meaning once +code has been scheduled to execute with `process.nextTick()`, the execution +cannot be stopped, just like with a normal function. Refer to [this guide](../topics/the-event-loop-timers-and-nexttick#processnexttick) +to better understand the operation of `process.nextTick()`. + +### "Infinite Loop" Execution ~ *`setInterval()`* + +If there is a block of code that should execute multiple times, `setInterval()` +can be used to execute that code. `setInterval()` takes a function +argument that will run an infinite number of times with a given millisecond +delay as the second argument. Just like `setTimeout()`, additional arguments +can be added beyond the delay, and these will be passed on to the function call. +Also like `setTimeout()`, the delay cannot be guaranteed because of operations +that may hold on to the event loop, and therefore should be treated as an +approximate delay. See the below example: + +```js +function intervalFunc () { + console.log('Cant stop me now!'); +} + +setInterval(intervalFunc, 1500); +``` +In the above example, `intervalFunc()` will execute about every 1500 +milliseconds, or 1.5 seconds, until it is stopped (see below). + +Just like `setTimeout()`, `setInterval()` also returns a `Timeout` object which +can be used to reference and modify the interval that was set. + +## Clearing the Future + +What can be done if a `Timeout` or `Immediate` object needs to be cancelled? +`setTimeout()`, `setImmediate()`, and `setInterval()` return a timer object +that can be used to reference the set `Timeout` or `Immediate` object. +By passing said object into the respective `clear` function, execution of +that object will be halted completely. The respective functions are +`clearTimeout()`, `clearImmediate()`, and `clearInterval()`. See the example +below for an example of each: + +```js +let timeoutObj = setTimeout(() => { + console.log('timeout beyond time'); +}, 1500); + +let immediateObj = setImmediate(() => { + console.log('immediately executing immediate'); +}); + +let intervalObj = setInterval(() => { + console.log('interviewing the interval'); +}, 500); + +clearTimeout(timeoutObj); +clearImmediate(immediateObj); +clearInterval(intervalObj); +``` + +## Leaving Timeouts Behind + +Remember that `Timeout` objects are returned by `setTimeout` and `setInterval`. +The `Timeout` object provides two functions intended to augment `Timeout` +behavior with `unref()` and `ref()`. If there is a `Timeout` object scheduled +using a `set` function, `unref()` can be called on that object. This will change +the behavior slightly, and not call the `Timeout` object *if it is the last +code to execute*. The `Timeout` object will not keep the process alive, waiting +to execute. + +In similar fashion, a `Timeout` object that has had `unref()` called on it +can remove that behavior by calling `ref()` on that same `Timeout` object, +which will then ensure its execution. Be aware, however, that this does +not *exactly* restore the initial behavior for performance reasons. See +below for examples of both: + +```js +let timerObj = setTimeout(() => { + console.log('will i run?'); +}); + +// if left alone, this statement will keep the above +// timeout from running, since the timeout will be the only +// thing keeping the program from exiting +timerObj.unref(); + +// we can bring it back to life by calling ref() inside +// an immediate +setImmediate(() => { + timerObj.ref(); +}); +``` +## Further Down the Event Loop + +There's much more to the Event Loop and Timers than this guide +has covered. To learn more about the internals of the Node.js +Event Loop and how Timers operate during execution, check out +this Node.js guide: [The Node.js Event Loop, Timers, and +process.nextTick()](../topics/the-event-loop-timers-and-nexttick). diff --git a/doc/topics/the-event-loop-timers-and-nexttick.md b/doc/topics/the-event-loop-timers-and-nexttick.md index 0876edeab4244b..b6499f5168e506 100644 --- a/doc/topics/the-event-loop-timers-and-nexttick.md +++ b/doc/topics/the-event-loop-timers-and-nexttick.md @@ -4,7 +4,7 @@ The event loop is what allows Node.js to perform non-blocking I/O operations — despite the fact that JavaScript is single-threaded — by -offloading operations to the system kernel whenever possible. +offloading operations to the system kernel whenever possible. Since most modern kernels are multi-threaded, they can handle multiple operations executing in the background. When one of these operations @@ -12,7 +12,7 @@ completes, the kernel tells Node.js so that the appropriate callback may added to the `poll` queue to eventually be executed. We'll explain this in further detail later in this topic. -## Event Loop Explained +## Event Loop Explained When Node.js starts, it initializes the event loop, processes the provided input script (or drops into the REPL, which is not covered in @@ -67,7 +67,7 @@ actually uses are those above._ ## Phases Overview: -* `timers`: this phase executes callbacks scheduled by `setTimeout()` +* `timers`: this phase executes callbacks scheduled by `setTimeout()` and `setInterval()`. * `I/O callbacks`: most types of callback except timers, setImmedate, close * `idle, prepare`: only used internally @@ -88,13 +88,13 @@ _may be executed_ rather than the **exact** time a person _wants it to be executed_. Timers callbacks will run as early as they can be scheduled after the specified amount of time has passed; however, Operating System scheduling or the running of other callbacks may delay -them. +them. -_**Note**: Technically, the [`poll` phase](#poll) controls when timers +_**Note**: Technically, the [`poll` phase](#poll) controls when timers are executed._ -For example, say you schedule a timeout to execute after a 100 ms -threshold, then your script starts asynchronously reading a file which +For example, say you schedule a timeout to execute after a 100 ms +threshold, then your script starts asynchronously reading a file which takes 95 ms: ```js @@ -102,8 +102,8 @@ takes 95 ms: var fs = require('fs'); function someAsyncOperation (callback) { - - // let's assume this takes 95ms to complete + + // let's assume this takes 95ms to complete fs.readFile('/path/to/file', callback); } @@ -149,12 +149,12 @@ more events. ### I/O callbacks: -This phase executes callbacks for some system operations such as types +This phase executes callbacks for some system operations such as types of TCP errors. For example if a TCP socket receives `ECONNREFUSED` when attempting to connect, some \*nix systems want to wait to report the error. This will be queued to execute in the `I/O callbacks` phase. -### poll: +### poll: The poll phase has two main functions: @@ -171,7 +171,7 @@ either the queue has been exhausted, or the system-dependent hard limit is reached. * _If the `poll` queue **is empty**_, one of two more things will -happen: +happen: * If scripts have been scheduled by `setImmediate()`, the event loop will end the `poll` phase and continue to the `check` phase to execute those scheduled scripts. @@ -202,7 +202,7 @@ etc. However, after a callback has been scheduled with `setImmediate()`, then the `poll` phase becomes idle, it will end and continue to the `check` phase rather than waiting for `poll` events. -### `close callbacks`: +### `close callbacks`: If a socket or handle is closed abruptly (e.g. `socket.destroy()`), the `'close'` event will be emitted in this phase. Otherwise it will be @@ -211,10 +211,10 @@ emitted via `process.nextTick()`. ## `setImmediate()` vs `setTimeout()` `setImmediate` and `setTimeout()` are similar, but behave in different -ways depending on when they are called. +ways depending on when they are called. * `setImmediate()` is designed to execute a script once the current -`poll` phase completes. +`poll` phase completes. * `setTimeout()` schedules a script to be run after a minimum threshold in ms has elapsed. @@ -379,7 +379,7 @@ We have two calls that are similar as far as users are concerned, but their names are confusing. * `process.nextTick()` fires immediately on the same phase -* `setImmediate()` fires on the following iteration or 'tick' of the +* `setImmediate()` fires on the following iteration or 'tick' of the event loop In essence, the names should be swapped. `process.nextTick()` fires more @@ -393,7 +393,7 @@ While they are confusing, the names themselves won't change. easier to reason about (and it leads to code that's compatible with a wider variety of environments, like browser JS.)* -## Why use `process.nextTick()`? +## Why use `process.nextTick()`? There are two main reasons: @@ -420,7 +420,7 @@ the event loop to proceed it must hit the `poll` phase, which means there is a non-zero chance that a connection could have been received allowing the connection event to be fired before the listening event. -Another example is running a function constructor that was to, say, +Another example is running a function constructor that was to, say, inherit from `EventEmitter` and it wanted to call an event within the constructor: @@ -440,10 +440,10 @@ myEmitter.on('event', function() { }); ``` -You can't emit an event from the constructor immediately -because the script will not have processed to the point where the user -assigns a callback to that event. So, within the constructor itself, -you can use `process.nextTick()` to set a callback to emit the event +You can't emit an event from the constructor immediately +because the script will not have processed to the point where the user +assigns a callback to that event. So, within the constructor itself, +you can use `process.nextTick()` to set a callback to emit the event after the constructor has finished, which provides the expected results: ```js