@@ -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