diff --git a/packages/ddp-server/livedata_server.js b/packages/ddp-server/livedata_server.js index a79d0febf2..cd7a24094c 100644 --- a/packages/ddp-server/livedata_server.js +++ b/packages/ddp-server/livedata_server.js @@ -1794,7 +1794,6 @@ Object.assign(Server.prototype, { const options = args[0]?.hasOwnProperty('returnStubValue') ? args.shift() : {}; - DDP._CurrentMethodInvocation._set(); DDP._CurrentMethodInvocation._setCallAsyncMethodRunning(true); const promise = new Promise((resolve, reject) => { DDP._CurrentCallAsyncInvocation._set({ name, hasCallAsyncParent: true }); diff --git a/packages/email/email_tests.js b/packages/email/email_tests.js index 05cfde3414..1decde3438 100644 --- a/packages/email/email_tests.js +++ b/packages/email/email_tests.js @@ -18,7 +18,7 @@ TEST_CASES.forEach(({ title, options, testCalls }) => { testCall(test, stream); }); }); - Promise.all(allPromises).then(() => resolver()); + Promise.all(allPromises).then(() => resolver()).catch(console.error); }); await promise; }); diff --git a/packages/meteor/async_helpers.js b/packages/meteor/async_helpers.js index 3d3d92dd79..240915eeed 100644 --- a/packages/meteor/async_helpers.js +++ b/packages/meteor/async_helpers.js @@ -171,3 +171,5 @@ const _sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); Meteor._sleepForMs = function (ms) { return _sleep(ms); }; + +Meteor.sleep = _sleep; \ No newline at end of file diff --git a/packages/meteor/dynamics_nodejs.js b/packages/meteor/dynamics_nodejs.js index 1e3f2b287a..aa83ae548b 100644 --- a/packages/meteor/dynamics_nodejs.js +++ b/packages/meteor/dynamics_nodejs.js @@ -2,10 +2,6 @@ let nextSlot = 0; let callAsyncMethodRunning = false; -const CURRENT_VALUE_KEY_NAME = "currentValue"; -const UPPER_CALL_DYNAMICS_KEY_NAME = "upperCallDynamics"; - -const SLOT_CALL_KEY = "slotCall"; /** * @memberOf Meteor * @summary Constructor for EnvironmentVariable @@ -26,12 +22,11 @@ class EnvironmentVariableAsync { * @returns {any} The current value of the variable, or `undefined` if no */ get() { - if (this.slot !== Meteor._getValueFromAslStore(SLOT_CALL_KEY)) { - const dynamics = Meteor._getValueFromAslStore(UPPER_CALL_DYNAMICS_KEY_NAME) || {}; + let store = Meteor._getAslStore(); - return dynamics[this.slot]; + if (store && store.dynamics) { + return store.dynamics[this.slot]; } - return Meteor._getValueFromAslStore(CURRENT_VALUE_KEY_NAME); } getOrNullIfOutsideFiber() { @@ -48,48 +43,31 @@ class EnvironmentVariableAsync { * @returns {Promise} The return value of the function */ withValue(value, func, options = {}) { - const self = this; - const slotCall = self.slot; - const dynamics = Object.assign( - {}, - Meteor._getValueFromAslStore(UPPER_CALL_DYNAMICS_KEY_NAME) || {} - ); + let store = Meteor._getAslStore(); + let dynamics = store && store.dynamics ? store.dynamics.slice() : []; + dynamics[this.slot] = value; - if (slotCall != null) { - dynamics[slotCall] = value; + let newStore = { dynamics: dynamics }; + + if (options) { + Object.assign(newStore, options); } - return Meteor._runAsync( - function () { - Meteor._updateAslStore(CURRENT_VALUE_KEY_NAME, value); - Meteor._updateAslStore(UPPER_CALL_DYNAMICS_KEY_NAME, dynamics); - return func(); - }, - self, - Object.assign( - { - callId: `${this.slot}-${Math.random()}`, - [SLOT_CALL_KEY]: this.slot, - }, - options, - ), - ); + return Meteor._getAsl().run(newStore, func); } - _set(context) { - const _meteor_dynamics = - Meteor._getValueFromAslStore("_meteor_dynamics") || []; - _meteor_dynamics[this.slot] = context; + _set(value) { + const dynamics = Meteor._getValueFromAslStore('dynamics') || []; + dynamics[this.slot] = value; } _setNewContextAndGetCurrent(value) { - let _meteor_dynamics = Meteor._getValueFromAslStore("_meteor_dynamics"); - if (!_meteor_dynamics) { - _meteor_dynamics = []; - } + const dynamics = Meteor._getValueFromAslStore('dynamics') || []; + + const saved = dynamics[this.slot]; + + dynamics[this.slot] = value; - const saved = _meteor_dynamics[this.slot]; - this._set(value); return saved; } @@ -145,8 +123,8 @@ Meteor.EnvironmentVariable = EnvironmentVariableAsync; * @return {Function} The wrapped function */ Meteor.bindEnvironment = (func, onException, _this) => { - const dynamics = Meteor._getValueFromAslStore(CURRENT_VALUE_KEY_NAME); - const currentSlot = Meteor._getValueFromAslStore(SLOT_CALL_KEY); + let store = Meteor._getAsl().getStore(); + let dynamics = store && store.dynamics ? store.dynamics.slice() : []; if (!onException || typeof onException === "string") { var description = onException || "callback of async function"; @@ -162,37 +140,21 @@ Meteor.bindEnvironment = (func, onException, _this) => { return function (/* arguments */) { var args = Array.prototype.slice.call(arguments); - var runWithEnvironment = function () { - return Meteor._runAsync( - () => { - let ret; - try { - if (currentSlot) { - Meteor._updateAslStore(CURRENT_VALUE_KEY_NAME, dynamics); - } - ret = func.apply(_this, args); + return Meteor._getAsl().run({ + dynamics: dynamics + }, function () { + let ret; + try { + ret = func.apply(_this, args); - // Using this strategy to be consistent between client and server and stop always returning a promise from the server - if (Meteor._isPromise(ret)) { - ret = ret.catch(onException); - } - } catch (e) { - onException(e); - } - return ret; - }, - _this, - { - callId: `bindEnvironment-${Math.random()}`, - [SLOT_CALL_KEY]: currentSlot, + // Using this strategy to be consistent between client and server and stop always returning a promise from the server + if (Meteor._isPromise(ret)) { + ret = ret.catch(onException); } - ); - }; - - if (Meteor._getAslStore()) { - return runWithEnvironment(); - } - - return Meteor._getAsl().run({}, runWithEnvironment); + } catch (e) { + onException(e); + } + return ret; + }); }; }; diff --git a/packages/meteor/dynamics_test.js b/packages/meteor/dynamics_test.js index e33a28ad97..2c33177705 100644 --- a/packages/meteor/dynamics_test.js +++ b/packages/meteor/dynamics_test.js @@ -183,6 +183,53 @@ Tinytest.addAsync("environment - bare bindEnvironment", * This won't work on the client due to the absence of ALS/AH */ if (Meteor.isServer) { + Tinytest.addAsync("environment - defer and environment variables", async function (test) { + const varA = new Meteor.EnvironmentVariable("a"); + const varB = new Meteor.EnvironmentVariable("b"); + + let deferOnly = null; + + varA.withValue(1, () => { + varB.withValue(2, () => { + Meteor.defer(() => { + console.log('Defer', varA.get(), varB.get()); + + deferOnly = [varA.get(), varB.get()]; + }); + }); + }); + + let deferWithBindEnv = null; + + varA.withValue(1, () => { + varB.withValue(2, () => { + Meteor.defer( + Meteor.bindEnvironment(() => { + console.log('Defer + Bind', varA.get(), varB.get()); + + deferWithBindEnv = [varA.get(), varB.get()]; + }) + ); + }); + }); + + let raw = null; + + varA.withValue(1, () => { + varB.withValue(2, () => { + console.log('Raw:', varA.get(), varB.get()); + + raw = [varA.get(), varB.get()]; + }); + }); + + await Meteor.sleep(100); + + test.equal(deferOnly, [1, 2]); + test.equal(deferWithBindEnv, [1, 2]); + test.equal(raw, [1, 2]); + }) + Tinytest.addAsync('environment - preserve ev value async/await', async function (test) { let val1 = null; let val2 = null;