Skip to content

Commit fe4fbc2

Browse files
committed
doc: add docs for AsyncListeners
Documentation has been added on how to use the AsyncListener API.
1 parent 005cc05 commit fe4fbc2

2 files changed

Lines changed: 216 additions & 2 deletions

File tree

doc/api/net.markdown

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,9 @@ already been bound to a port or domain socket.
174174
Listening on a file descriptor is not supported on Windows.
175175

176176
This function is asynchronous. When the server has been bound,
177-
['listening'](#event_listening_) event will be emitted.
177+
['listening'][] event will be emitted.
178178
the last parameter `callback` will be added as an listener for the
179-
['listening'](#event_listening_) event.
179+
['listening'][] event.
180180

181181
### server.close([callback])
182182

doc/api/process.markdown

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,4 +647,218 @@ a diff reading, useful for benchmarks and measuring intervals:
647647
// benchmark took 1000000527 nanoseconds
648648
}, 1000);
649649

650+
651+
## Async Listeners
652+
653+
<!-- type=misc -->
654+
655+
Stability: 1 - Experimental
656+
657+
The `AsyncListener` API is the JavaScript interface for the `AsyncWrap`
658+
class which allows developers to be notified about key events in the
659+
lifetime of an asynchronous event. Node performs a lot of asynchronous
660+
events internally, and significant use of this API will have a **dramatic
661+
performance impact** on your application.
662+
663+
664+
## process.createAsyncListener(asyncListener[, callbacksObj[, storageValue]])
665+
666+
* `asyncListener` {Function} callback fired when an asynchronous event is
667+
instantiated.
668+
* `callbacksObj` {Object} optional callbacks that will fire at specific
669+
times in the lifetime of the asynchronous event.
670+
* `storageValue` {Value} a value that will be passed as the first argument
671+
when the `asyncListener` callback is run, and to all subsequent callback.
672+
673+
Returns a constructed `AsyncListener` object.
674+
675+
To begin capturing asynchronous events pass the object to
676+
[`process.addAsyncListener()`][]. The same `AsyncListener` instance can
677+
only be added once to the active queue, and subsequent attempts to add the
678+
instance will be ignored.
679+
680+
To stop capturing pass the object to [`process.removeAsyncListener()`][].
681+
This does _not_ mean the `AsyncListener` previously added will stop
682+
triggering callbacks. Once attached to an asynchronous event it will
683+
persist with the lifetime of the asynchronous call stack.
684+
685+
Explanation of function parameters:
686+
687+
`asyncListener(storageValue)`: A `Function` called when an asynchronous
688+
event is instantiated. If a `Value` is returned then it will be attached
689+
to the event and overwrite any value that had been passed to
690+
`process.createAsyncListener()`'s `storageValue` argument. If an initial
691+
`storageValue` was passed when created, then `asyncListener()` will
692+
receive that as a function argument.
693+
694+
`callbacksObj`: An `Object` which may contain three optional fields:
695+
696+
* `before(context, storageValue)`: A `Function` that is called immediately
697+
before the asynchronous callback is about to run. It will be passed both
698+
the `context` (i.e. `this`) of the calling function and the `storageValue`
699+
either returned from `asyncListener` or passed during construction (if
700+
either occurred).
701+
702+
* `after(context, storageValue)`: A `Function` called immediately after
703+
the asynchronous event's callback has run. Note this will not be called
704+
if the callback throws and the error is not handled.
705+
706+
* `error(storageValue, error)`: A `Function` called if the event's
707+
callback threw. If `error` returns `true` then Node will assume the error
708+
has been properly handled and resume execution normally. When multiple
709+
`error()` callbacks have been registered, only **one** of those callbacks
710+
needs to return `true` for `AsyncListener` to accept that the error has
711+
been handled.
712+
713+
`storageValue`: A `Value` (i.e. anything) that will be, by default,
714+
attached to all new event instances. This will be overwritten if a `Value`
715+
is returned by `asyncListener()`.
716+
717+
Here is an example of overwriting the `storageValue`:
718+
719+
process.createAsyncListener(function listener(value) {
720+
// value === true
721+
return false;
722+
}, {
723+
before: function before(context, value) {
724+
// value === false
725+
}
726+
}, true);
727+
728+
**Note:** The [EventEmitter][], while used to emit status of an asynchronous
729+
event, is not itself asynchronous. So `asyncListener()` will not fire when
730+
an event is added, and `before`/`after` will not fire when emitted
731+
callbacks are called.
732+
733+
734+
## process.addAsyncListener(asyncListener[, callbacksObj[, storageValue]])
735+
## process.addAsyncListener(asyncListener)
736+
737+
Returns a constructed `AsyncListener` object and immediately adds it to
738+
the listening queue to begin capturing asynchronous events.
739+
740+
Function parameters can either be the same as
741+
[`process.createAsyncListener()`][], or a constructed `AsyncListener`
742+
object.
743+
744+
Example usage for capturing errors:
745+
746+
var cntr = 0;
747+
var key = process.addAsyncListener(function() {
748+
return { uid: cntr++ };
749+
}, {
750+
before: function onBefore(context, storage) {
751+
// Need to remove the listener while logging or will end up
752+
// with an infinite call loop.
753+
process.removeAsyncListener(key);
754+
console.log('uid: %s is about to run', storage.uid);
755+
process.addAsyncListener(key);
756+
},
757+
after: function onAfter(context, storage) {
758+
process.removeAsyncListener(key);
759+
console.log('uid: %s is about to run', storage.uid);
760+
process.addAsyncListener(key);
761+
},
762+
error: function onError(storage, err) {
763+
// Handle known errors
764+
if (err.message === 'really, it\'s ok') {
765+
process.removeAsyncListener(key);
766+
console.log('handled error just threw:');
767+
console.log(err.stack);
768+
process.addAsyncListener(key);
769+
return true;
770+
}
771+
}
772+
});
773+
774+
process.nextTick(function() {
775+
throw new Error('really, it\'s ok');
776+
});
777+
778+
// Output:
779+
// uid: 0 is about to run
780+
// handled error just threw:
781+
// Error: really, it's ok
782+
// at /tmp/test2.js:27:9
783+
// at process._tickCallback (node.js:583:11)
784+
// at Function.Module.runMain (module.js:492:11)
785+
// at startup (node.js:123:16)
786+
// at node.js:1012:3
787+
788+
## process.removeAsyncListener(asyncListener)
789+
790+
Removes the `AsyncListener` from the listening queue.
791+
792+
Removing the `AsyncListener` from the queue does _not_ mean asynchronous
793+
events called during its execution scope will stop firing callbacks. Once
794+
attached to an event it will persist for the entire asynchronous call
795+
stack. For example:
796+
797+
var key = process.createAsyncListener(function asyncListener() {
798+
// To log we must stop listening or we'll enter infinite recursion.
799+
process.removeAsyncListener(key);
800+
console.log('You summoned me?');
801+
process.addAsyncListener(key);
802+
});
803+
804+
// We want to begin capturing async events some time in the future.
805+
setTimeout(function() {
806+
process.addAsyncListener(key);
807+
808+
// Perform a few additional async events.
809+
setTimeout(function() {
810+
setImmediate(function() {
811+
process.nextTick(function() { });
812+
});
813+
});
814+
815+
// Removing the listener doesn't mean to stop capturing events that
816+
// have already been added.
817+
process.removeAsyncListener(key);
818+
}, 100);
819+
820+
// Output:
821+
// You summoned me?
822+
// You summoned me?
823+
// You summoned me?
824+
// You summoned me?
825+
826+
The fact that we logged 4 asynchronous events is an implementation detail
827+
of Node's [Timers][].
828+
829+
To stop capturing from a specific asynchronous event stack
830+
`process.removeAsyncListener()` must be called from within the call
831+
stack itself. For example:
832+
833+
var key = process.createAsyncListener(function asyncListener() {
834+
// To log we must stop listening or we'll enter infinite recursion.
835+
process.removeAsyncListener(key);
836+
console.log('You summoned me?');
837+
process.addAsyncListener(key);
838+
});
839+
840+
// We want to begin capturing async events some time in the future.
841+
setTimeout(function() {
842+
process.addAsyncListener(key);
843+
844+
// Perform a few additional async events.
845+
setImmediate(function() {
846+
// Stop capturing from this call stack.
847+
process.removeAsyncListener(key);
848+
849+
process.nextTick(function() { });
850+
});
851+
}, 100);
852+
853+
// Output:
854+
// You summoned me?
855+
856+
The user must be explicit and always pass the `AsyncListener` they wish
857+
to remove. It is not possible to simply remove all listeners at once.
858+
859+
650860
[EventEmitter]: events.html#events_class_events_eventemitter
861+
[Timers]: timers.html
862+
[`process.createAsyncListener()`]: #process_process_createasynclistener_asynclistener_callbacksobj_storagevalue
863+
[`process.addAsyncListener()`]: #process_process_addasynclistener_asynclistener
864+
[`process.removeAsyncListener()`]: #process_process_removeasynclistener_asynclistener

0 commit comments

Comments
 (0)