From 324a6c235a5bfcbcd7cc7491d55461915c10af34 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 4 Jan 2021 10:57:45 -0800 Subject: [PATCH] async_hooks: add thisArg to AsyncResource.bind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Semver-major Support setting the `thisArg` for AsyncResource.bind and AsyncResource.prototype.bind. If `thisArg` is not set, then `this` will be set to the `AsyncResource` instance. Fixes: https://github.com/nodejs/node/issues/36051 Signed-off-by: James M Snell PR-URL: https://github.com/nodejs/node/pull/36782 Reviewed-By: Juan José Arboleda Reviewed-By: Matteo Collina Reviewed-By: Michaël Zasso Reviewed-By: Gerhard Stöbich --- doc/api/async_hooks.md | 14 ++++++++++++-- lib/async_hooks.js | 13 +++++++++---- test/parallel/test-asyncresource-bind.js | 16 ++++++++++++++++ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/doc/api/async_hooks.md b/doc/api/async_hooks.md index a353ef3b7b9f8f..4a8a225c576fdb 100644 --- a/doc/api/async_hooks.md +++ b/doc/api/async_hooks.md @@ -730,30 +730,40 @@ class DBQuery extends AsyncResource { } ``` -#### Static method: `AsyncResource.bind(fn[, type])` +#### Static method: `AsyncResource.bind(fn[, type, [thisArg]])` * `fn` {Function} The function to bind to the current execution context. * `type` {string} An optional name to associate with the underlying `AsyncResource`. +* `thisArg` {any} Binds the given function to the current execution context. The returned function will have an `asyncResource` property referencing the `AsyncResource` to which the function is bound. -#### `asyncResource.bind(fn)` +#### `asyncResource.bind(fn[, thisArg])` * `fn` {Function} The function to bind to the current `AsyncResource`. +* `thisArg` {any} Binds the given function to execute to this `AsyncResource`'s scope. diff --git a/lib/async_hooks.js b/lib/async_hooks.js index 90b48ebe4b2754..c3bbdb1b795e87 100644 --- a/lib/async_hooks.js +++ b/lib/async_hooks.js @@ -220,10 +220,15 @@ class AsyncResource { return this[trigger_async_id_symbol]; } - bind(fn) { + bind(fn, thisArg = this) { if (typeof fn !== 'function') throw new ERR_INVALID_ARG_TYPE('fn', 'Function', fn); - const ret = FunctionPrototypeBind(this.runInAsyncScope, this, fn); + const ret = + FunctionPrototypeBind( + this.runInAsyncScope, + this, + fn, + thisArg); ObjectDefineProperties(ret, { 'length': { configurable: true, @@ -241,9 +246,9 @@ class AsyncResource { return ret; } - static bind(fn, type) { + static bind(fn, type, thisArg) { type = type || fn.name; - return (new AsyncResource(type || 'bound-anonymous-fn')).bind(fn); + return (new AsyncResource(type || 'bound-anonymous-fn')).bind(fn, thisArg); } } diff --git a/test/parallel/test-asyncresource-bind.js b/test/parallel/test-asyncresource-bind.js index 3aeed475aee126..a9f613d9302edf 100644 --- a/test/parallel/test-asyncresource-bind.js +++ b/test/parallel/test-asyncresource-bind.js @@ -33,3 +33,19 @@ setImmediate(() => { assert.strictEqual(asyncResource.asyncId(), fn2()); assert.notStrictEqual(asyncId, fn2()); }); + +const foo = {}; +const fn3 = asyncResource.bind(common.mustCall(function() { + assert.strictEqual(this, foo); +}), foo); +fn3(); + +const fn4 = asyncResource.bind(common.mustCall(function() { + assert.strictEqual(this, asyncResource); +})); +fn4(); + +const fn5 = asyncResource.bind(common.mustCall(function() { + assert.strictEqual(this, false); +}), false); +fn5();