413 lines
11 KiB
JavaScript
413 lines
11 KiB
JavaScript
|
|
var Tail = require('./tail');
|
|
|
|
module.exports = function() {
|
|
var procedure = function(fn) {
|
|
return function() {
|
|
var cb = arguments[arguments.length - 1];
|
|
|
|
try {
|
|
fn.apply(this, arguments);
|
|
} catch(e) {
|
|
handleError(e, cb);
|
|
}
|
|
};
|
|
};
|
|
|
|
var handleError = function(e, cb) {
|
|
cb(e);
|
|
};
|
|
|
|
var callback = function(cb, fn) {
|
|
var called = false;
|
|
|
|
return function(err) {
|
|
if (called) {
|
|
if (err) {
|
|
console.log(err);
|
|
if (err.stack) {
|
|
console.log(err.stack);
|
|
}
|
|
}
|
|
throw new Error('Continuation is being called more than once!');
|
|
}
|
|
called = true;
|
|
try {
|
|
if (err) {
|
|
handleError(err, cb);
|
|
} else {
|
|
fn.apply(this, arguments);
|
|
}
|
|
} catch(e) {
|
|
handleError(e, cb);
|
|
}
|
|
};
|
|
};
|
|
|
|
var _seq = procedure(function(procs, i, res, cb) {
|
|
if (i >= procs.length) {
|
|
return cb(null, res);
|
|
}
|
|
var proc = procs[i];
|
|
proc(res, callback(cb, function(err, res) {
|
|
// return _seq(procs, i+1, res, cb);
|
|
return Tail.run(function() {
|
|
_seq(procs, i+1, res, cb);
|
|
});
|
|
}));
|
|
});
|
|
|
|
var seq = function(procs, cb) {
|
|
return _seq(procs, 0, null, cb);
|
|
};
|
|
|
|
var rescue = procedure(function(procBundle, cb) {
|
|
var tryProc = procBundle['try'];
|
|
var catchProc = procBundle['catch'] || function(err, cb) {cb(err);};
|
|
var finallyProc = procBundle['finally'] || function(cb) {cb();};
|
|
|
|
var applyTry = procedure(function(cb) {
|
|
tryProc(cb);
|
|
});
|
|
|
|
var applyCatch = procedure(function(err, cb) {
|
|
catchProc(err, cb);
|
|
});
|
|
|
|
var applyFinallyOk = procedure(function(res0, cb) {
|
|
finallyProc(callback(cb, function(err, res) {
|
|
cb(null, res0);
|
|
}));
|
|
});
|
|
|
|
var applyFinallyError = procedure(function(err0, cb) {
|
|
finallyProc(callback(cb, function(err, res) {
|
|
cb(err0);
|
|
}));
|
|
});
|
|
|
|
applyTry(function(err, res) {
|
|
if (err) {
|
|
applyCatch(err, function(err, res) {
|
|
if (err) {
|
|
applyFinallyError(err, cb);
|
|
} else {
|
|
applyFinallyOk(res, cb);
|
|
}
|
|
});
|
|
} else {
|
|
applyFinallyOk(res, cb);
|
|
}
|
|
});
|
|
});
|
|
|
|
var pwhile = procedure(function(procBool, procBody, cb) {
|
|
seq([
|
|
function(_, cb) {
|
|
procBool(cb);
|
|
},
|
|
function(_, cb) {
|
|
if (_) {
|
|
seq([
|
|
function(_, cb) {
|
|
procBody(cb);
|
|
},
|
|
function(_, cb) {
|
|
pwhile(procBool, procBody, cb);
|
|
}
|
|
], cb);
|
|
} else {
|
|
cb();
|
|
}
|
|
}
|
|
], cb);
|
|
});
|
|
|
|
var peach = procedure(function(arr, proc, cb) {
|
|
var i = 0;
|
|
|
|
pwhile(
|
|
function(cb) {
|
|
cb(null, i < arr.length);
|
|
},
|
|
function(cb) {
|
|
seq([
|
|
function(_, cb) {
|
|
proc(arr[i], cb);
|
|
},
|
|
function(_, cb) {
|
|
i++;
|
|
cb();
|
|
}
|
|
], cb);
|
|
},
|
|
cb
|
|
)
|
|
});
|
|
|
|
var pfor = procedure(function(n, proc, cb) {
|
|
var i = 0;
|
|
|
|
pwhile(
|
|
function(cb) {
|
|
cb(null, i < n);
|
|
},
|
|
function(cb) {
|
|
seq([
|
|
function(_, cb) {
|
|
proc(i, cb);
|
|
},
|
|
function(_, cb) {
|
|
i++;
|
|
cb();
|
|
}
|
|
], cb);
|
|
},
|
|
cb
|
|
);
|
|
});
|
|
|
|
var pmap = procedure(function(arr, proc, cb) {
|
|
var l = [];
|
|
|
|
seq([
|
|
function(_, cb) {
|
|
peach(arr, function(e, cb) {
|
|
seq([
|
|
function(_, cb) {
|
|
proc(e, cb);
|
|
},
|
|
function(_, cb) {
|
|
l.push(_);
|
|
cb();
|
|
}
|
|
], cb);
|
|
}, cb);
|
|
},
|
|
function(_, cb) {
|
|
cb(null, l);
|
|
}
|
|
], cb);
|
|
});
|
|
|
|
var _parallel2 = procedure(function(proc1, proc2, cb) {
|
|
var state1 = 'start';
|
|
var state2 = 'start';
|
|
var res1;
|
|
var res2;
|
|
var err1;
|
|
var err2;
|
|
|
|
var applyProc1 = procedure(function(cb) {
|
|
proc1(cb);
|
|
});
|
|
|
|
var applyProc2 = procedure(function(cb) {
|
|
proc2(cb);
|
|
});
|
|
|
|
applyProc1(function(err, res) {Tail.run(function() {
|
|
if (err) {
|
|
state1 = 'error';
|
|
err1 = err;
|
|
switch(state2) {
|
|
case 'start':
|
|
break;
|
|
case 'done':
|
|
cb(null, [
|
|
{status: 'error', error: err1},
|
|
{status: 'ok', data: res2}
|
|
]);
|
|
break;
|
|
case 'error':
|
|
cb(null, [
|
|
{status: 'error', error: err1},
|
|
{status: 'error', error: err2}
|
|
]);
|
|
break;
|
|
default:
|
|
}
|
|
} else {
|
|
state1 = 'done';
|
|
res1 = res;
|
|
switch(state2) {
|
|
case 'start':
|
|
break;
|
|
case 'done':
|
|
cb(null, [
|
|
{status: 'ok', data: res1},
|
|
{status: 'ok', data: res2}
|
|
]);
|
|
break;
|
|
case 'error':
|
|
cb(null, [
|
|
{status: 'ok', data: res1},
|
|
{status: 'error', error: err2}
|
|
]);
|
|
break;
|
|
default:
|
|
}
|
|
}
|
|
})});
|
|
|
|
applyProc2(function(err, res) {Tail.run(function() {
|
|
if (err) {
|
|
state2 = 'error';
|
|
err2 = err;
|
|
switch(state1) {
|
|
case 'start':
|
|
break;
|
|
case 'done':
|
|
cb(null, [
|
|
{status: 'ok', data: res1},
|
|
{status: 'error', error: err2}
|
|
]);
|
|
break;
|
|
case 'error':
|
|
cb(null, [
|
|
{status: 'error', error: err1},
|
|
{status: 'error', error: err2}
|
|
]);
|
|
break;
|
|
default:
|
|
}
|
|
} else {
|
|
state2 = 'done';
|
|
res2 = res;
|
|
switch(state1) {
|
|
case 'start':
|
|
break;
|
|
case 'done':
|
|
cb(null, [
|
|
{status: 'ok', data: res1},
|
|
{status: 'ok', data: res2}
|
|
]);
|
|
break;
|
|
case 'error':
|
|
cb(null, [
|
|
{status: 'error', error: err1},
|
|
{status: 'ok', data: res2}
|
|
]);
|
|
break;
|
|
default:
|
|
}
|
|
}
|
|
})});
|
|
});
|
|
|
|
var _parallel = procedure(function(procs, i, cb) {
|
|
if (procs.length == 0) {
|
|
return cb();
|
|
}
|
|
|
|
if (i == procs.length - 1) {
|
|
return procs[i](function(err, res) {
|
|
if (err) {
|
|
cb(null, [{status: 'error', error: err}]);
|
|
} else {
|
|
cb(null, [{status: 'ok', data: res}]);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (i < procs.length) {
|
|
_parallel2(
|
|
procs[i],
|
|
function(cb) {
|
|
_parallel(procs, i+1, cb);
|
|
},
|
|
callback(cb, function(err, res) {
|
|
cb(null, [res[0]].concat(res[1].data));
|
|
})
|
|
);
|
|
}
|
|
});
|
|
|
|
var parallel = procedure(function(procs, cb) {
|
|
_parallel(procs, 0, cb);
|
|
});
|
|
|
|
var noFail = function() {
|
|
var proc, handler, cb;
|
|
|
|
proc = arguments[0];
|
|
cb = arguments[arguments.length - 1];
|
|
|
|
if (arguments.length == 2) {
|
|
handler = function(err) {
|
|
console.log('ERROR caught by cps.noFail: ', err);
|
|
if (err.stack) {
|
|
console.log(err.stack);
|
|
}
|
|
};
|
|
} else if (arguments.length == 3) {
|
|
handler = arguments[1];
|
|
} else {
|
|
handleError(new Error('Incorrect number of arguments in calling cps.noFail.'), cb);
|
|
}
|
|
|
|
rescue({
|
|
'try': function(cb) {
|
|
proc(cb);
|
|
},
|
|
'catch': function(err, cb) {
|
|
handler(err);
|
|
cb();
|
|
}
|
|
}, cb);
|
|
};
|
|
|
|
var run = function(proc, cfg) {
|
|
cfg = cfg || {};
|
|
|
|
var cb = function(err, res) {
|
|
try {
|
|
if (err) {
|
|
if (cfg['error']) {
|
|
cfg['error'](err);
|
|
} else {
|
|
console.log('cps.run ERROR: ', err);
|
|
if (err.stack) {
|
|
console.log(err.stack);
|
|
}
|
|
}
|
|
} else {
|
|
if (cfg['ok']) {
|
|
cfg['ok'](res);
|
|
} else {
|
|
console.log('cps.run OK: ', res);
|
|
}
|
|
}
|
|
} catch(e) {
|
|
if (cfg['topLevelError']) {
|
|
cfg['topLevelError'](e);
|
|
} else {
|
|
console.log('cps.run TOP_LEVEL_ERROR: ', e);
|
|
}
|
|
} finally {
|
|
if (cfg['finally']) {
|
|
try {
|
|
cfg['finally']();
|
|
} catch(e) {
|
|
console.log('cps.run FINALLY_ERROR: ', e);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
proc(cb);
|
|
};
|
|
|
|
return {
|
|
seq: seq,
|
|
peach: peach,
|
|
pwhile: pwhile,
|
|
pmap: pmap,
|
|
pfor: pfor,
|
|
rescue: rescue,
|
|
parallel: parallel,
|
|
noFail: noFail,
|
|
run: run
|
|
};
|
|
}();
|