diff --git a/packages/meteor/async_helpers.js b/packages/meteor/async_helpers.js index 3e1e5023ac..c07a9c22de 100644 --- a/packages/meteor/async_helpers.js +++ b/packages/meteor/async_helpers.js @@ -33,22 +33,25 @@ class AsynchronousQueue { this._draining = false; } - async queueTask(task) { - this._taskHandles.push({ + queueTask(task) { + const self = this; + self._taskHandles.push({ task: task, name: task.name }); - await this._scheduleRun(); + self._scheduleRun(); } - async _scheduleRun() { + _scheduleRun() { // Already running or scheduled? Do nothing. if (this._runningOrRunScheduled) return; this._runningOrRunScheduled = true; - await this._run(); + setImmediate(() => { + this._run(); + }); } async _run() { @@ -62,29 +65,56 @@ class AsynchronousQueue { return; } const taskHandle = this._taskHandles.shift(); - + let exception; // Run the task. try { await taskHandle.task(); } catch (err) { + if (taskHandle.resolver) { + // We'll throw this exception through runTask. + exception = err; + } else { Meteor._debug("Exception in queued task", err); + } } // Soon, run the next task, if there is any. this._runningOrRunScheduled = false; - await this._scheduleRun(); + this._scheduleRun(); + + if (taskHandle.resolver) { + if (exception) { + taskHandle.resolver(null, exception); + } else { + taskHandle.resolver(); + } + } } async runTask(task) { + let resolver; + const promise = new Promise( + (resolve, reject) => + (resolver = (res, rej) => { + if (rej) { + reject(rej); + return; + } + resolve(res); + }) + ); + const handle = { task: Meteor.bindEnvironment(task, function(e) { Meteor._debug('Exception from task', e); throw e; }), - name: task.name + name: task.name, + resolver, }; this._taskHandles.push(handle); await this._scheduleRun(); + return promise; } flush() { diff --git a/packages/meteor/dynamics_test.js b/packages/meteor/dynamics_test.js index 3fd795a30d..dacbb04294 100644 --- a/packages/meteor/dynamics_test.js +++ b/packages/meteor/dynamics_test.js @@ -16,10 +16,10 @@ Tinytest.add("environment - dynamic variables", function (test) { test.equal(CurrentFoo.get(), undefined); }); -Tinytest.add("environment - bindEnvironment", function (test) { +Tinytest.addAsync("environment - bindEnvironment", async function (test) { var raised_f; - var f = CurrentFoo.withValue(17, function () { + var f = await CurrentFoo.withValue(17, function () { return Meteor.bindEnvironment(function (flag) { test.equal(CurrentFoo.get(), 17); if (flag) @@ -31,24 +31,24 @@ Tinytest.add("environment - bindEnvironment", function (test) { }); }); - var test_f = function () { + var test_f = async function () { raised_f = null; - test.equal(f(false), 12); + test.equal(await f(false), 12); test.equal(raised_f, null); - test.equal(f(true), undefined); + test.equal(await f(true), undefined); test.equal(raised_f, "test"); }; // At top level test.equal(CurrentFoo.get(), undefined); - test_f(); + await test_f(); // Inside a withValue - CurrentFoo.withValue(22, function () { + await CurrentFoo.withValue(22, function () { test.equal(CurrentFoo.get(), 22); test_f(); test.equal(CurrentFoo.get(), 22); @@ -61,7 +61,7 @@ Tinytest.add("environment - bindEnvironment", function (test) { var raised_g; - var g = CurrentFoo.withValue(99, function () { + var g = await CurrentFoo.withValue(99, function () { return Meteor.bindEnvironment(function (flag) { test.equal(CurrentFoo.get(), 99); @@ -76,19 +76,19 @@ Tinytest.add("environment - bindEnvironment", function (test) { }); }); - var test_g = function () { + var test_g = async function () { raised_g = null; - test.equal(g(false), 88); + test.equal(await g(false), 88); test.equal(raised_g, null); - test.equal(g(true), undefined); + test.equal(await g(true), undefined); test.equal(raised_g, "trial"); }; - test_g(); + await test_g(); - CurrentFoo.withValue(77, function () { + await CurrentFoo.withValue(77, function () { test.equal(CurrentFoo.get(), 77); test_g(); test.equal(CurrentFoo.get(), 77); diff --git a/packages/meteor/fiber_helpers_test.js b/packages/meteor/fiber_helpers_test.js index 84093ee1d7..8b71de19c6 100644 --- a/packages/meteor/fiber_helpers_test.js +++ b/packages/meteor/fiber_helpers_test.js @@ -1,7 +1,5 @@ -var Fiber = Npm.require('fibers'); - -Tinytest.add("fibers - synchronous queue", function (test) { - var q = new Meteor._SynchronousQueue; +Tinytest.addAsync("asl-sync - synchronous queue", async function (test) { + var q = new Meteor._SynchronousQueue(); var output = []; var pusher = function (n) { return function () { @@ -20,14 +18,12 @@ Tinytest.add("fibers - synchronous queue", function (test) { q.queueTask(pusher(1)); outputIsUpTo(0); - // Run another task. After queueing it, the fiber constructed here will yield - // back to this outer function. No task can have run yet since the main test - // fiber still will not have yielded. + // Run another task async to be solved in the future. var runTask2Done = false; - Fiber(function () { - q.runTask(pusher(2)); + Meteor._runAsync(async function () { + await q.runTask(pusher(2)); runTask2Done = true; - }).run(); + }); outputIsUpTo(0); test.isFalse(runTask2Done); @@ -43,12 +39,12 @@ Tinytest.add("fibers - synchronous queue", function (test) { // Run a task and block for it to be done. All queued tasks up to this one // will now be run. - q.runTask(pusher(4)); + await q.runTask(pusher(4)); outputIsUpTo(4); test.isTrue(runTask2Done); // Task #5 is still in the queue. Run another task synchronously. - q.runTask(pusher(6)); + await q.runTask(pusher(6)); outputIsUpTo(6); // Queue a task that throws. It'll write some debug output, but that's it. @@ -57,13 +53,13 @@ Tinytest.add("fibers - synchronous queue", function (test) { throw new Error("bla"); }); // let it run. - q.runTask(pusher(7)); + await q.runTask(pusher(7)); outputIsUpTo(7); // Run a task that throws. It should throw from runTask. Meteor._suppress_log(1); - test.throws(function () { - q.runTask(function () { + await test.throwsAsync(async function () { + await q.runTask(function () { throw new Error("this is thrown"); }); }); diff --git a/packages/meteor/package.js b/packages/meteor/package.js index a05aed1339..eeb5e979b2 100644 --- a/packages/meteor/package.js +++ b/packages/meteor/package.js @@ -74,7 +74,6 @@ Package.onTest(function (api) { api.addFiles('dynamics_test.js', ['client', 'server']); api.addFiles('fiber_helpers_test.js', ['server']); - api.addFiles('wrapasync_test.js', ['server']); api.addFiles('url_tests.js', ['client', 'server']); diff --git a/packages/meteor/wrapasync_test.js b/packages/meteor/wrapasync_test.js deleted file mode 100644 index aa8469e946..0000000000 --- a/packages/meteor/wrapasync_test.js +++ /dev/null @@ -1,89 +0,0 @@ -var asyncFunction1 = function (x, cb) { - setTimeout(function () { cb(null, x); }, 5); -}; -var asyncFunction2 = function (x, opt, cb) { - if (! cb && opt instanceof Function) { - cb = opt; - opt = null; - } - asyncFunction1(x, cb); -}; -var asyncFunction3 = function (opt, cb) { - if (! cb && opt instanceof Function) { - cb = opt; - opt = null; - } - asyncFunction1(3, cb); -}; -var asyncFunction4 = function (cb) { - asyncFunction1(3, cb); -}; - -var asyncFunction5 = function (cb) { - var self = this; - setTimeout(function() { - cb(null, self); - }, 5); -} -asyncFunction5.context = {}; - -var wrapped1 = Meteor.wrapAsync(asyncFunction1); -var wrapped2 = Meteor.wrapAsync(asyncFunction2); -var wrapped3 = Meteor.wrapAsync(asyncFunction3); -var wrapped4 = Meteor.wrapAsync(asyncFunction4); -var wrapped5 = Meteor.wrapAsync( - asyncFunction5, - asyncFunction5.context -); - -Tinytest.add("environment - wrapAsync sync", function (test) { - // one required arg and callback - test.equal(wrapped1(3), 3); - test.equal(wrapped1(3, undefined), 3); - // one required arg, optional second arg, callback - test.equal(wrapped2(3), 3); - test.equal(wrapped2(3, {foo: "bar"}), 3); - test.equal(wrapped2(3, undefined, undefined), 3); - test.equal(wrapped2(3, {foo: "bar"}, undefined), 3); - // optional first arg, callback - test.equal(wrapped3(3), 3); - test.equal(wrapped3(3, undefined), 3); - test.equal(wrapped3(), 3); - test.equal(wrapped3(undefined), 3); - // only callback - test.equal(wrapped4(), 3); - test.equal(wrapped4(undefined), 3); - test.equal(wrapped5(), asyncFunction5.context); -}); - -testAsyncMulti("environment - wrapAsync async", [ - function (test, expect) { - var cb = function (result) { - return expect(null, result); - }; - // one required arg and callback - test.equal(wrapped1(3, cb(3)), undefined); - // one required arg, optional second arg, callback - test.equal(wrapped2(3, cb(3)), undefined); - test.equal(wrapped2(3, {foo: "bar"}, cb(3)), undefined); - test.equal(wrapped2(3, undefined, cb(3)), undefined); - // optional first arg, callback - test.equal(wrapped3(3, cb(3)), undefined); - test.equal(wrapped3(cb(3)), undefined); - test.equal(wrapped3(undefined, cb(3)), undefined); - // only callback - test.equal(wrapped4(cb(3)), undefined); - } -]); - -Tinytest.addAsync("environment - wrapAsync callback is " + - "in fiber", function (test, onComplete) { - var cb = function (err, result) { - if (Meteor.isServer) { - var Fiber = Npm.require('fibers'); - test.isTrue(Fiber.current); - } - onComplete(); - }; - wrapped1(3, cb); - });