From 337059bf6d3e10a966eff97769dd77a441a29f32 Mon Sep 17 00:00:00 2001 From: Tomi Belan Date: Sat, 24 Aug 2013 15:13:19 -0700 Subject: [PATCH 1/2] Fix throwing errors into the generator Fixes issue #5 --- run.js | 11 +++++------ test.js | 30 +++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/run.js b/run.js index 74b1a93..e505aa8 100644 --- a/run.js +++ b/run.js @@ -10,10 +10,10 @@ function run(generator, callback) { next(); check(); - function nextSafe(item) { + function nextSafe(err, item) { var n; try { - n = iterator.next(item); + n = (err ? iterator.throw(err) : iterator.next(item)); if (!n.done) { if (typeof n.value === "function") n.value(resume()); yielded = true; @@ -26,8 +26,8 @@ function run(generator, callback) { return callback(null, n.value); } - function nextPlain(item) { - var cont = iterator.next(item).value; + function nextPlain(err, item) { + var cont = (err ? iterator.throw(err) : iterator.next(item)).value; // Pass in resume to continuables if one was yielded. if (typeof cont === "function") cont(resume()); yielded = true; @@ -49,8 +49,7 @@ function run(generator, callback) { var item = data[1]; data = null; yielded = false; - if (err) return iterator.throw(err); - next(item); + next(err, item); yielded = true; } } diff --git a/test.js b/test.js index 32bde81..fa5eaa0 100644 --- a/test.js +++ b/test.js @@ -70,7 +70,7 @@ function *run_with_callback(gen) { console.log("Callback"); yield run(function* (gen) { yield sleep(1000); - return "Hello" + return "Hello"; }, function (err, value) { console.log("Callback err: " + err); console.log("Callback value: " + value); @@ -102,7 +102,25 @@ function *run_with_callback_early_exception(gen) { console.log("Callback value: " + value); gen()(null, value); // Intentionally suppress the error }); - console.log("End"); + testRun("run_with_thrown_error", run_with_thrown_error); +} + +function *run_with_thrown_error(gen) { + var inCatch = false; + try { + yield sleep(1); + yield fail(); + console.error("this should not happen!"); + } + catch (err) { + console.log("in catch: " + err); + console.assert(err); + inCatch = true; + } + yield sleep(1); + console.log("yielded after catch"); + console.assert(inCatch); + console.log("\nEnd"); } function sleep(ms) { @@ -117,7 +135,13 @@ function evil() { setTimeout(function () { callback(null, 2); }, 100); - } + }; +} + +function fail() { + return function (callback) { + callback(Error("throwing error into generator")); + }; } function decrement(n) { From 52b86fd0b153168f63919426572e14b051039141 Mon Sep 17 00:00:00 2001 From: Tomi Belan Date: Sat, 24 Aug 2013 17:34:01 -0700 Subject: [PATCH 2/2] Always use an implicit done callback. --- run.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/run.js b/run.js index e505aa8..f3997f0 100644 --- a/run.js +++ b/run.js @@ -5,12 +5,17 @@ function run(generator, callback) { var iterator = generator(resume); var data = null, yielded = false; - var next = callback ? nextSafe : nextPlain; - + if (!callback) callback = function (err) { + // If the generator ended with an error, throw it globally with setTimeout. + // Throwing locally from a callback is not allowed, and swallowing the + // error is a bad idea, so there's no better option. + if (err) setTimeout(function () { throw err; }, 0); + }; + next(); check(); - function nextSafe(err, item) { + function next(err, item) { var n; try { n = (err ? iterator.throw(err) : iterator.next(item)); @@ -25,13 +30,6 @@ function run(generator, callback) { } return callback(null, n.value); } - - function nextPlain(err, item) { - var cont = (err ? iterator.throw(err) : iterator.next(item)).value; - // Pass in resume to continuables if one was yielded. - if (typeof cont === "function") cont(resume()); - yielded = true; - } function resume() { var done = false;