Support exceptions and promises in gen-thread

master
Vitaliy Filippov 2016-07-21 19:11:05 +03:00
parent 46ad19fe2a
commit ab29b85cf3
2 changed files with 66 additions and 12 deletions

View File

@ -21,8 +21,6 @@ function* test(thread)
return 'result'; return 'result';
} }
gen.run(test, null, function(result) { console.log(result); });
function* test_throttle(thread) function* test_throttle(thread)
{ {
yield thread.throttle(5); yield thread.throttle(5);
@ -38,5 +36,25 @@ function* other_gen(thread)
console.log('finished in another generator'); console.log('finished in another generator');
} }
for (var i = 0; i < 15; i++) function* test_throw(thread)
gen.run(test_throttle); {
var cb = thread.errorfirst();
try
{
yield setTimeout(function() { cb(new Error()); }, 500);
}
catch (e)
{
console.log('Catched '+e.stack);
}
console.log(yield setTimeout(thread.cb(), 500));
console.log('sleep');
console.log(yield setTimeout(thread.cb(), 500));
console.log('continue');
}
//gen.run(test, null, function(result) { console.log(result); });
//for (var i = 0; i < 15; i++) gen.run(test_throttle);
gen.run(test_throw);

View File

@ -1,7 +1,7 @@
// Yet Another Hack to fight node.js callback hell: generator-based coroutines // Yet Another Hack to fight node.js callback hell: generator-based coroutines
// Distinctive features: // Distinctive features:
// - simple to use: does not require modifications of existing callback-based code // - simple to use: does not require modifications of existing callback or promise based code
// - checks control flow safely // - safely checks control flow
module.exports.run = runThread; module.exports.run = runThread;
module.exports.runParallel = runParallel; module.exports.runParallel = runParallel;
@ -11,6 +11,7 @@ function runThread(generator, arg, finishCallback)
var thread = function() { continueThread.apply(thread) }; var thread = function() { continueThread.apply(thread) };
thread.throttle = throttleThread; thread.throttle = throttleThread;
thread.cb = threadCallback.bind(thread); thread.cb = threadCallback.bind(thread);
thread.errorfirst = errorFirst.bind(thread);
thread._gen = generator(thread, arg); thread._gen = generator(thread, arg);
thread._finishThrottleQueue = finishThrottleQueue.bind(thread); thread._finishThrottleQueue = finishThrottleQueue.bind(thread);
thread._finishCallback = finishCallback; thread._finishCallback = finishCallback;
@ -21,11 +22,15 @@ function runThread(generator, arg, finishCallback)
function continueThread() function continueThread()
{ {
// pass parameters as yield result // pass parameters as yield result
var pass = Array.prototype.slice.call(arguments, 0); callGen(this, 'next', Array.prototype.slice.call(arguments, 0));
}
function callGen(thread, method, arg)
{
var v; var v;
try try
{ {
v = this._gen.next(pass); v = thread._gen[method](arg);
} }
catch (e) catch (e)
{ {
@ -34,13 +39,22 @@ function continueThread()
if (v.done) if (v.done)
{ {
// generator finished // generator finished
this._done = true; thread._done = true;
process.nextTick(this._finishThrottleQueue); process.nextTick(thread._finishThrottleQueue);
} }
if (v.error) if (v.error)
throw v.error; throw v.error;
if (v.done && this._finishCallback) if (v.done && thread._finishCallback)
this._finishCallback(v.value); thread._finishCallback(v.value);
if (typeof v.value == 'object' && v.value.then)
{
// check if v.value is a Promise
var cb = thread.cb();
v.value.then(cb, function(error)
{
callGen(thread, 'throw', error);
});
}
} }
function threadCallback() function threadCallback()
@ -63,6 +77,28 @@ function threadCallback()
return fn; return fn;
} }
function errorFirst()
{
var thread = this;
var fn = function()
{
if (thread._current != fn)
{
throw new Error('Broken control flow! Callback'+
thread._current._stack.replace(/^\s*Error\s*at Function\.thread\.cb\s*\([^)]*\)/, '')+
'\nmust be called to resume thread, but this one is called instead:'+
fn._stack.replace(/^\s*Error\s*at Function\.thread\.cb\s*\([^)]*\)/, '')+'\n--'
);
}
if (arguments[0])
return callGen(thread, 'throw', arguments[0]);
return callGen(thread, Array.prototype.slice.call(arguments, 0));
};
fn._stack = new Error().stack;
thread._current = fn;
return fn;
}
function throttleThread(count) function throttleThread(count)
{ {
if (!this.throttleData) if (!this.throttleData)