@@ -859,6 +859,293 @@ for (let i = 0; i < 10; i++) {
859859}
860860```
861861
862+ ## Class: ` AsyncLocalStorage `
863+ <!-- YAML
864+ added: REPLACEME
865+ -->
866+
867+ This class is used to create asynchronous state within callbacks and promise
868+ chains. It allows storing data throughout the lifetime of a web request
869+ or any other asynchronous duration. It is similar to thread-local storage
870+ in other languages.
871+
872+ The following example builds a logger that will always know the current HTTP
873+ request and uses it to display enhanced logs without needing to explicitly
874+ provide the current HTTP request to it.
875+
876+ ``` js
877+ const { AsyncLocalStorage } = require (' async_hooks' );
878+ const http = require (' http' );
879+
880+ const kReq = ' CURRENT_REQUEST' ;
881+ const asyncLocalStorage = new AsyncLocalStorage ();
882+
883+ function log (... args ) {
884+ const store = asyncLocalStorage .getStore ();
885+ // Make sure the store exists and it contains a request.
886+ if (store && store .has (kReq)) {
887+ const req = store .get (kReq);
888+ // Prints `GET /items ERR could not do something
889+ console .log (req .method , req .url , ... args);
890+ } else {
891+ console .log (... args);
892+ }
893+ }
894+
895+ http .createServer ((request , response ) => {
896+ asyncLocalStorage .run (() => {
897+ const store = asyncLocalStorage .getStore ();
898+ store .set (kReq, request);
899+ someAsyncOperation ((err , result ) => {
900+ if (err) {
901+ log (' ERR' , err .message );
902+ }
903+ });
904+ });
905+ })
906+ .listen (8080 );
907+ ```
908+
909+ When having multiple instances of ` AsyncLocalStorage ` , they are independent
910+ from each other. It is safe to instantiate this class multiple times.
911+
912+ ### ` new AsyncLocalStorage() `
913+ <!-- YAML
914+ added: REPLACEME
915+ -->
916+
917+ Creates a new instance of ` AsyncLocalStorage ` . Store is only provided within a
918+ ` run ` or a ` runSyncAndReturn ` method call.
919+
920+ ### ` asyncLocalStorage.disable() `
921+ <!-- YAML
922+ added: REPLACEME
923+ -->
924+
925+ This method disables the instance of ` AsyncLocalStorage ` . All subsequent calls
926+ to ` asyncLocalStorage.getStore() ` will return ` undefined ` until
927+ ` asyncLocalStorage.run() ` or ` asyncLocalStorage.runSyncAndReturn() `
928+ is called again.
929+
930+ When calling ` asyncLocalStorage.disable() ` , all current contexts linked to the
931+ instance will be exited.
932+
933+ Calling ` asyncLocalStorage.disable() ` is required before the
934+ ` asyncLocalStorage ` can be garbage collected. This does not apply to stores
935+ provided by the ` asyncLocalStorage ` , as those objects are garbage collected
936+ along with the corresponding async resources.
937+
938+ This method is to be used when the ` asyncLocalStorage ` is not in use anymore
939+ in the current process.
940+
941+ ### ` asyncLocalStorage.getStore() `
942+ <!-- YAML
943+ added: REPLACEME
944+ -->
945+
946+ * Returns: {Map}
947+
948+ This method returns the current store.
949+ If this method is called outside of an asynchronous context initialized by
950+ calling ` asyncLocalStorage.run ` or ` asyncLocalStorage.runAndReturn ` , it will
951+ return ` undefined ` .
952+
953+ ### ` asyncLocalStorage.run(callback[, ...args]) `
954+ <!-- YAML
955+ added: REPLACEME
956+ -->
957+
958+ * ` callback ` {Function}
959+ * ` ...args ` {any}
960+
961+ Calling ` asyncLocalStorage.run(callback) ` will create a new asynchronous
962+ context.
963+ Within the callback function and the asynchronous operations from the callback,
964+ ` asyncLocalStorage.getStore() ` will return an instance of ` Map ` known as
965+ "the store". This store will be persistent through the following
966+ asynchronous calls.
967+
968+ The callback will be ran asynchronously. Optionally, arguments can be passed
969+ to the function. They will be passed to the callback function.
970+
971+ If an error is thrown by the callback function, it will not be caught by
972+ a ` try/catch ` block as the callback is ran in a new asynchronous resource.
973+ Also, the stacktrace will be impacted by the asynchronous call.
974+
975+ Example:
976+
977+ ``` js
978+ asyncLocalStorage .run (() => {
979+ asyncLocalStorage .getStore (); // Returns a Map
980+ someAsyncOperation (() => {
981+ asyncLocalStorage .getStore (); // Returns the same Map
982+ });
983+ });
984+ asyncLocalStorage .getStore (); // Returns undefined
985+ ```
986+
987+ ### ` asyncLocalStorage.exit(callback[, ...args]) `
988+ <!-- YAML
989+ added: REPLACEME
990+ -->
991+
992+ * ` callback ` {Function}
993+ * ` ...args ` {any}
994+
995+ Calling ` asyncLocalStorage.exit(callback) ` will create a new asynchronous
996+ context.
997+ Within the callback function and the asynchronous operations from the callback,
998+ ` asyncLocalStorage.getStore() ` will return ` undefined ` .
999+
1000+ The callback will be ran asynchronously. Optionally, arguments can be passed
1001+ to the function. They will be passed to the callback function.
1002+
1003+ If an error is thrown by the callback function, it will not be caught by
1004+ a ` try/catch ` block as the callback is ran in a new asynchronous resource.
1005+ Also, the stacktrace will be impacted by the asynchronous call.
1006+
1007+ Example:
1008+
1009+ ``` js
1010+ asyncLocalStorage .run (() => {
1011+ asyncLocalStorage .getStore (); // Returns a Map
1012+ asyncLocalStorage .exit (() => {
1013+ asyncLocalStorage .getStore (); // Returns undefined
1014+ });
1015+ asyncLocalStorage .getStore (); // Returns the same Map
1016+ });
1017+ ```
1018+
1019+ ### ` asyncLocalStorage.runSyncAndReturn(callback[, ...args]) `
1020+ <!-- YAML
1021+ added: REPLACEME
1022+ -->
1023+
1024+ * ` callback ` {Function}
1025+ * ` ...args ` {any}
1026+
1027+ This methods runs a function synchronously within a context and return its
1028+ return value. The store is not accessible outside of the callback function or
1029+ the asynchronous operations created within the callback.
1030+
1031+ Optionally, arguments can be passed to the function. They will be passed to
1032+ the callback function.
1033+
1034+ If the callback function throws an error, it will be thrown by
1035+ ` runSyncAndReturn ` too. The stacktrace will not be impacted by this call and
1036+ the context will be exited.
1037+
1038+ Example:
1039+
1040+ ``` js
1041+ try {
1042+ asyncLocalStorage .runSyncAndReturn (() => {
1043+ asyncLocalStorage .getStore (); // Returns a Map
1044+ throw new Error ();
1045+ });
1046+ } catch (e) {
1047+ asyncLocalStorage .getStore (); // Returns undefined
1048+ // The error will be caught here
1049+ }
1050+ ```
1051+
1052+ ### ` asyncLocalStorage.exitSyncAndReturn(callback[, ...args]) `
1053+ <!-- YAML
1054+ added: REPLACEME
1055+ -->
1056+
1057+ * ` callback ` {Function}
1058+ * ` ...args ` {any}
1059+
1060+ This methods runs a function synchronously outside of a context and return its
1061+ return value. The store is not accessible within the callback function or
1062+ the asynchronous operations created within the callback.
1063+
1064+ Optionally, arguments can be passed to the function. They will be passed to
1065+ the callback function.
1066+
1067+ If the callback function throws an error, it will be thrown by
1068+ ` exitSyncAndReturn ` too. The stacktrace will not be impacted by this call and
1069+ the context will be re-entered.
1070+
1071+ Example:
1072+
1073+ ``` js
1074+ // Within a call to run or runSyncAndReturn
1075+ try {
1076+ asyncLocalStorage .getStore (); // Returns a Map
1077+ asyncLocalStorage .exitSyncAndReturn (() => {
1078+ asyncLocalStorage .getStore (); // Returns undefined
1079+ throw new Error ();
1080+ });
1081+ } catch (e) {
1082+ asyncLocalStorage .getStore (); // Returns the same Map
1083+ // The error will be caught here
1084+ }
1085+ ```
1086+
1087+ ### Choosing between ` run ` and ` runSyncAndReturn `
1088+
1089+ #### When to choose ` run `
1090+
1091+ ` run ` is asynchronous. It is called with a callback function that
1092+ runs within a new asynchronous call. This is the most explicit behavior as
1093+ everything that is executed within the callback of ` run ` (including further
1094+ asynchronous operations) will have access to the store.
1095+
1096+ If an instance of ` AsyncLocalStorage ` is used for error management (for
1097+ instance, with ` process.setUncaughtExceptionCaptureCallback ` ), only
1098+ exceptions thrown in the scope of the callback function will be associated
1099+ with the context.
1100+
1101+ This method is the safest as it provides strong scoping and consistent
1102+ behavior.
1103+
1104+ It cannot be promisified using ` util.promisify ` . If needed, the ` Promise `
1105+ constructor can be used:
1106+
1107+ ``` js
1108+ new Promise ((resolve , reject ) => {
1109+ asyncLocalStorage .run (() => {
1110+ someFunction ((err , result ) => {
1111+ if (err) {
1112+ return reject (err);
1113+ }
1114+ return resolve (result);
1115+ });
1116+ });
1117+ });
1118+ ```
1119+
1120+ #### When to choose ` runSyncAndReturn `
1121+
1122+ ` runSyncAndReturn ` is synchronous. The callback function will be executed
1123+ synchronously and its return value will be returned by ` runSyncAndReturn ` .
1124+ The store will only be accessible from within the callback
1125+ function and the asynchronous operations created within this scope.
1126+ If the callback throws an error, ` runSyncAndReturn ` will throw it and it will
1127+ not be associated with the context.
1128+
1129+ This method provides good scoping while being synchronous.
1130+
1131+ #### Usage with ` async/await `
1132+
1133+ If, within an async function, only one ` await ` call is to run within a context,
1134+ the following pattern should be used:
1135+
1136+ ``` js
1137+ async function fn () {
1138+ await asyncLocalStorage .runSyncAndReturn (() => {
1139+ asyncLocalStorage .getStore ().set (' key' , value);
1140+ return foo (); // The return value of foo will be awaited
1141+ });
1142+ }
1143+ ```
1144+
1145+ In this example, the store is only available in the callback function and the
1146+ functions called by ` foo ` . Outside of ` runSyncAndReturn ` , calling ` getStore `
1147+ will return ` undefined ` .
1148+
8621149[ `after` callback ] : #async_hooks_after_asyncid
8631150[ `before` callback ] : #async_hooks_before_asyncid
8641151[ `destroy` callback ] : #async_hooks_destroy_asyncid
0 commit comments