diff --git a/README.md b/README.md index 5c3cbda..5e4e402 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,42 @@ getBar(function(error, result){ }); ``` +## Immediately execute + +You can force a righto task for run at any time without dealing with the results (or error) by calling +it with no arguments: + +``` +// Lazily resolve (won't run untill called) +var something = righto(getSomething); + +// Force something to start resolving *now* +something(); + +// later ... + +something(function(error, result){ + // handle error or use result. +}); + +``` + +Also, since righto tasks return themselves when called, you can do this a little more shorthand, like so: + + + +``` +// Immediately force the righto to begin resolving. +var something = righto(getSomething)(); // <= note the call with no arguments. + +// later ... + +something(function(error, result){ + // handle error or use result. +}); + +``` + ## Take / Multiple results By default, dependent tasks are passed only the first result of a dependency `righto`. eg: diff --git a/example/asyncAwaitCompare/index.browser.js b/example/asyncAwaitCompare/index.browser.js index 36ae6fa..eb6e70a 100644 --- a/example/asyncAwaitCompare/index.browser.js +++ b/example/asyncAwaitCompare/index.browser.js @@ -36,104 +36,403 @@ var addTextToPage = require('./addTextToPage'); // Righto version of this: http://jakearchibald.com/2014/es7-async-functions/ -var righto = require('../'); +var righto = require('../../'); function loadStory(){ - var getStory = righto(getJSON, 'story.json'), - addHeading = righto.sync(story => addHtmlToPage(story.heading), getStory), - addChapters = righto.all(righto.sync(story => - story.chapterURLs.map(chapterUrl => righto(getJSON, chapterUrl)) - .reduce((result, getChapter) => righto.sync(addHtmlToPage, getChapter, [result]), null) - , getStory)); - - righto.all(addHeading, addChapters)(error => { + var story = righto(getJSON, 'story.json'); + + var headingAdded = story.get(story => addHtmlToPage(story.heading)); + + var chaptersAdded = righto.reduce(story.get(story => story.chapterURLs.map(chapterUrl => + righto(getJSON, chapterUrl)().get(addHtmlToPage) + ))); + + righto.reduce([headingAdded, chaptersAdded])(error => { + error ? + addTextToPage("Argh, broken: " + error.message) : + addTextToPage('All done'); document.querySelector('.spinner').style.display = 'none'; - error ? addTextToPage("Argh, broken: " + error.message) : addTextToPage('All done'); }); } window.addEventListener('load', loadStory); -},{"../":5,"./addHtmlToPage":1,"./addTextToPage":2,"./getJSON":3}],5:[function(require,module,exports){ -var foreign = require('foreign'), - cpsenize = require('cpsenize'); +},{"../../":5,"./addHtmlToPage":1,"./addTextToPage":2,"./getJSON":3}],5:[function(require,module,exports){ +var abbott = require('abbott'); + +var defer = typeof setImmediate !== 'undefined' ? setImmediate : setTimeout; function isRighto(x){ - return typeof x === 'function' && x.get === x; + return typeof x === 'function' && (x.__resolve__ === x || x.resolve === x); } -function slice(list, start, end){ - return Array.prototype.slice.call(list, start, end); +function isThenable(x){ + return x && typeof x.then === 'function' && !isRighto(x); } -function righto(fn){ - var args = slice(arguments, 1), - context = this, - started = 0, - callbacks = [], - results; +function isResolvable(x){ + return isRighto(x) || isThenable(x); +} + +function isTake(x){ + return x && typeof x === 'object' && '__take__' in x; +} + +var slice = Array.prototype.slice.call.bind(Array.prototype.slice); + +function getCallLine(stack){ + var index = 0, + lines = stack.split('\n'); + + while(lines[++index] && lines[index].match(/righto\/index\.js/)){} + + var match = lines[index] && lines[index].match(/at (.*)/); + + return match ? match[1] : ' - No trace - '; +} + +function resolveDependency(task, done){ + if(isThenable(task)){ + task = righto(abbott(task)); + } + + if(isRighto(task)){ + return task(function(error){ + var results = slice(arguments, 1, 2); + + if(!results.length){ + results.push(undefined); + } + + done(error, results); + }); + } + + function take(targetTask){ + var keys = slice(arguments, 1); + return targetTask(function(error){ + var args = slice(arguments, 1); + done(error, keys.map(function(key){ + return args[key]; + })); + }); + } - function get(callback){ - if(results){ - return callback.apply(context, results); + if( + righto._debug && + righto._warnOnUnsupported && + Array.isArray(task) && + isRighto(task[0]) && + !isRighto(task[1]) + ){ + + console.warn('\u001b[33mPossible unsupported take/ignore syntax detected:\u001b[39m\n' + getCallLine(this._stack)); + } + + if(isTake(task)){ + return take.apply(null, task.__take__); + } + + return done(null, [task]); +} + +function traceGet(instance, result){ + if(righto._debug && !(typeof result === 'object' || typeof result === 'function')){ + var line = getCallLine(instance._stack); + throw new Error('Result of righto was not an instance at: \n' + line); + } +} + +function get(fn){ + var instance = this; + return righto(function(result, fn, done){ + if(typeof fn === 'string'){ + traceGet(instance, result); + return done(null, result[fn]); } - callbacks.push(callback); + righto.from(fn(result))(done); + }, this, fn); +} - if(started++){ - return; +var noOp = function(){}; + +function proxy(instance){ + instance._ = new Proxy(instance, { + get: function(target, key){ + if(key === '__resolve__'){ + return instance._; + } + + if(instance[key] || key in instance || key === 'inspect' || typeof key === 'symbol'){ + return instance[key]; + } + + if(righto._debug && key.charAt(0) === '_'){ + return instance[key]; + } + + return proxy(righto.sync(function(result){ + traceGet(instance, result); + return result[key]; + }, instance)); } + }); + instance.__resolve__ = instance._; + return instance._; +} +function resolveIterator(fn){ + return function(){ + var args = slice(arguments), + callback = args.pop(), + errored, + lastValue; - foreign.parallel(function(task, done){ - if(isRighto(task)){ - return task(function(error){ - done(error, slice(arguments, 1)); - }); + function reject(error){ + if(errored){ + return; } + errored = true; + callback(error); + } - if(Array.isArray(task) && isRighto(task[0]) && !isRighto(task[1])){ - return task[0](function(error){ - var args = slice(arguments, 1); - done(error, task.slice(1).map(function(key){ - return args[key]; - })); + var generator = fn.apply(null, args.concat(reject)); + + function run(){ + if(errored){ + return; + } + var next = generator.next(lastValue); + if(next.done){ + if(errored){ + return; + } + return callback(null, next.value); + } + if(isResolvable(next.value)){ + righto.sync(function(value){ + lastValue = value; + run(); + }, next.value)(function(error){ + if(error){ + reject(error); + } }); + return; } + lastValue = next.value; + run(); + } - return done(null, task); - }, args, function(error, argResults){ - if(error){ - return callbacks.forEach(function(callback){ - callback(error); - }); + run(); + }; +} + +function addTracing(resolve, fn, args){ + + var argMatch = fn.toString().match(/^[\w\s]*?\(((?:\w+[,\s]*?)*)\)/), + argNames = argMatch ? argMatch[1].split(/[,\s]+/g) : []; + + resolve._stack = new Error().stack; + resolve._trace = function(tabs){ + var firstLine = getCallLine(resolve._stack); + + if(resolve._error){ + firstLine = '\u001b[31m' + firstLine + ' <- ERROR SOURCE' + '\u001b[39m'; + } + + tabs = tabs || 0; + var spacing = ' '; + for(var i = 0; i < tabs; i ++){ + spacing = spacing + ' '; + } + return args.map(function(arg, index){ + return [arg, argNames[index] || index]; + }).reduce(function(results, argInfo){ + var arg = argInfo[0], + argName = argInfo[1]; + + if(isTake(arg)){ + arg = arg.__take__[0]; } - argResults = [].concat.apply([], argResults); + if(isRighto(arg)){ + var line = spacing + '- argument "' + argName + '" from '; - argResults.push(function(){ - results = arguments; - callbacks.forEach(function(callback){ - callback.apply(context, results); - }); - }); - fn.apply(null, argResults); - }); + if(!arg._trace){ + line = line + 'Tracing was not enabled for this righto instance.'; + }else{ + line = line + arg._trace(tabs + 1); + } + results.push(line); + } + + return results; + }, [firstLine]) + .join('\n'); }; +} + +function taskComplete(error){ + var done = this[0], + context = this[1]; + + if(error && righto._debug){ + context.resolve._error = error; + } + + var results = arguments; + + done(results); + context.callbacks.forEach(function(callback){ + callback.apply(null, results); + }); +} + +function errorOut(error, callback){ + if(error && righto._debug){ + if(righto._autotraceOnError || this.resolve._traceOnError){ + console.log('Dependency error executing ' + this.fn.name + ' ' + this.resolve._trace()); + } + } + + callback(error); +} + +function resolveWithDependencies(done, error, argResults){ + var context = this; - get.get = get; + if(error){ + return context.callbacks.forEach(errorOut.bind(context, error)); + } - return get; + var args = [].concat.apply([], argResults), + complete = taskComplete.bind([done, context]); + + // Slight perf bump by avoiding apply for simple cases. + switch(args.length){ + case 0: context.fn(complete); break; + case 1: context.fn(args[0], complete); break; + case 2: context.fn(args[0], args[1], complete); break; + default: + args.push(complete); + context.fn.apply(null, args); + } +} + +function resolveDependencies(args, complete, resolveDependency){ + var results = [], + done = 0, + hasErrored; + + if(!args.length){ + complete(null, []); + } + + function dependencyResolved(index, error, result){ + if(hasErrored){ + return; + } + + if(error){ + hasErrored = true; + return complete(error); + } + + results[index] = result; + + if(++done === args.length){ + complete(null, results); + } + } + + args.forEach(function(arg, index){ + resolveDependency(arg, dependencyResolved.bind(null, index)); + }); +} + +function resolver(callback){ + var context = this, + complete = callback; + + // No callback? Just run the task. + if(!arguments.length){ + complete = noOp; + } + + if(typeof complete !== 'function'){ + throw new Error('Callback must be a function'); + } + + if(context.results){ + return complete.apply(null, context.results); + } + + if(righto._debug){ + if(righto._autotrace || this.resolve._traceOnExecute){ + console.log('Executing ' + context.fn.name + ' ' + this.resolve._trace()); + } + } + + context.callbacks.push(complete); + + if(this.started++){ + return; + } + + var complete = resolveWithDependencies.bind(context, function(resolvedResults){ + context.results = resolvedResults; + }); + + defer(resolveDependencies.bind(null, context.args, complete, resolveDependency.bind(this.resolve))); + + return this.resolve; +}; + +function righto(){ + var args = slice(arguments), + fn = args.shift(); + + if(typeof fn !== 'function'){ + throw new Error('No task function passed to righto'); + } + + var resolverContext = { + fn: fn, + callbacks: [], + args: args, + started: 0 + }, + resolve = resolver.bind(resolverContext); + resolve.get = get.bind(resolve); + resolverContext.resolve = resolve; + resolve.resolve = resolve; + + if(righto._debug){ + addTracing(resolve, fn, args); + } + + return resolve; } righto.sync = function(fn){ - return righto.apply(null, [cpsenize(fn)].concat(slice(arguments, 1))); + return righto.apply(null, [function(){ + var args = slice(arguments), + done = args.pop(); + + defer(function(){ + done(null, fn.apply(null, args)); + }); + }].concat(slice(arguments, 1))); }; -righto.all = function(task){ +righto.all = function(value){ + var task = value; if(arguments.length > 1){ task = slice(arguments); } + function resolve(tasks){ return righto.apply(null, [function(){ arguments[arguments.length - 1](null, slice(arguments, 0, -1)); @@ -149,120 +448,187 @@ righto.all = function(task){ return resolve(task); }; -module.exports = righto; -},{"cpsenize":6,"foreign":7}],6:[function(require,module,exports){ - -function cpsenize(fn){ - return function(){ - var args = Array.prototype.slice.call(arguments), - callback = args.pop(), - context = this, - result, - error; +righto.reduce = function(values, reducer, seed){ + return righto.from(values).get(function(values){ - try { - result = fn.apply(context, args); - } - catch(exception){ - error = exception; + if(!values || !values.reduce){ + throw new Error('values was not a reduceable object (like an array)'); } - callback(error, result); - }; -} + values = values.slice(); -module.exports = cpsenize; -},{}],7:[function(require,module,exports){ -function parallel(fn, items, callback){ - if(!items || typeof items !== 'object'){ - throw new Error('Items must be an object or an array'); - } + if(arguments.length < 3){ + seed = righto(values.shift()); + } - var keys = Object.keys(items), - isArray = Array.isArray(items), - length = isArray ? items.length : keys.length, - finalResult = new items.constructor(), - done = 0, - errored; + return values.reduce(function(previous, next){ + if(reducer){ + return righto(function(previous, done){ + reducer(previous, next)(done); + }, previous); + } - if(length === 0){ - return callback(null, finalResult); - } + return righto(done => next(done), righto.after(righto.from(previous))); + }, seed); + }); +}; - function isDone(key){ - return function(error, result){ +righto.from = function(value){ + if(isRighto(value)){ + return value; + } - if(errored){ - return; - } + if(!isResolvable(value) && typeof value === 'function'){ + value = value.apply(null, slice(arguments, 1)); + } - if(error){ - errored = true; - return callback(error); - } + return righto.sync(function(resolved){ + return resolved; + }, value); +}; - finalResult[key] = arguments.length > 2 ? Array.prototype.slice.call(arguments, 1) : result; +righto.mate = function(){ + return righto.apply(null, [function(){ + arguments[arguments.length -1].apply(null, [null].concat(slice(arguments, 0, -1))); + }].concat(slice(arguments))); +}; - if(++done === length){ - callback(null, finalResult); - } - }; +righto.take = function(task){ + if(!isResolvable(task)){ + throw new Error('task was not a resolvable value'); } - for (var i = 0; i < length; i++) { - var key = keys[i]; - if(isArray && isNaN(key)){ - continue; - } + return {__take__: slice(arguments)}; +}; - fn(items[key], isDone(key)); +righto.after = function(task){ + if(!isResolvable(task)){ + throw new Error('task was not a resolvable value'); } -} -function series(fn, items, callback){ - if(!items || typeof items !== 'object'){ - throw new Error('Items must be an object or an array'); + if(arguments.length === 1){ + return {__take__: [task]}; } - var keys = Object.keys(items), - isArray = Array.isArray(items), - length = isArray ? items.length : keys.length, - finalResult = new items.constructor(); + return {__take__: [righto.mate.apply(null, arguments)]}; +}; + +righto.resolve = function(object, deep){ + if(isRighto(object)){ + return righto.sync(function(object){ + return righto.resolve(object, deep); + }, object); + } - if(length === 0){ - return callback(null, finalResult); + if(!object || !(typeof object === 'object' || typeof object === 'function')){ + return righto.from(object); } - function next(index){ - var key = keys[index]; + var pairs = righto.all(Object.keys(object).map(function(key){ + return righto(function(value, done){ + if(deep){ + righto.sync(function(value){ + return [key, value]; + }, righto.resolve(value, true))(done); + return; + } + done(null, [key, value]); + }, object[key]); + })); + + return righto.sync(function(pairs){ + return pairs.reduce(function(result, pair){ + result[pair[0]] = pair[1]; + return result; + }, Array.isArray(object) ? [] : {}); + }, pairs); +}; - index++; +righto.iterate = function(){ + var args = slice(arguments), + fn = args.shift(); - if(isArray && isNaN(key)){ - return next(index); - } + return righto.apply(null, [resolveIterator(fn)].concat(args)); +}; - fn(items[keys[key]], function (error, result) { - if(error){ - return callback(error); - } +righto.value = function(){ + var args = arguments; + return righto(function(done){ + done.apply(null, [null].concat(slice(args))); + }); +}; + +righto.surely = function(task){ + if(!isResolvable(task)){ + task = righto.apply(null, arguments); + } - finalResult[key] = arguments.length > 2 ? Array.prototype.slice.call(arguments, 1) : result; + return righto(function(done){ + task(function(){ + done(null, slice(arguments)); + }); + }); +}; - if(index === length){ - return callback(null, finalResult); +righto.handle = function(task, handler){ + return righto(function(handler, done){ + task(function(error){ + if(!error){ + return task(done); } - next(index); + handler(error, done); }); + }, handler); +}; + +righto.fail = function(error){ + return righto(function(error, done){ + done(error); + }, error); +}; + +righto.isRighto = isRighto; +righto.isThenable = isThenable; +righto.isResolvable = isResolvable; + +righto.proxy = function(){ + if(typeof Proxy === 'undefined'){ + throw new Error('This environment does not support Proxy\'s'); } - next(0); + return proxy(righto.apply(this, arguments)); +}; + +for(var key in righto){ + righto.proxy[key] = righto[key]; +} + +module.exports = righto; +},{"abbott":6}],6:[function(require,module,exports){ +function checkIfPromise(promise){ + if(!promise || typeof promise !== 'object' || typeof promise.then !== 'function'){ + throw "Abbott requires a promise to break. It is the only thing Abbott is good at."; + } } -module.exports = { - parallel: parallel, - series: series +module.exports = function abbott(promiseOrFn){ + if(typeof promiseOrFn !== 'function'){ + checkIfPromise(promiseOrFn); + } + + return function(){ + var promise; + if(typeof promiseOrFn === 'function'){ + promise = promiseOrFn.apply(null, Array.prototype.slice.call(arguments, 0, -1)); + }else{ + promise = promiseOrFn; + } + + checkIfPromise(promise); + + var callback = arguments[arguments.length-1]; + promise.then(callback.bind(null, null), callback); + }; }; -},{}]},{},[4]) -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJleGFtcGxlL2FkZEh0bWxUb1BhZ2UuanMiLCJleGFtcGxlL2FkZFRleHRUb1BhZ2UuanMiLCJleGFtcGxlL2dldEpTT04uanMiLCJleGFtcGxlL2luZGV4LmpzIiwiaW5kZXguanMiLCJub2RlX21vZHVsZXMvY3BzZW5pemUvaW5kZXguanMiLCJub2RlX21vZHVsZXMvZm9yZWlnbi9pbmRleC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTs7QUNGQTtBQUNBO0FBQ0E7O0FDRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDckJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3BCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCJtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKGh0bWwpe1xuICAgIGRvY3VtZW50LmJvZHkuaW5uZXJIVE1MICs9IGh0bWw7XG59IiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbih0ZXh0KXtcbiAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKHRleHQpKTtcbn0iLCJ2YXIgZmlsZXMgPSB7XG4gICAgJ3N0b3J5Lmpzb24nOiB7XG4gICAgICAgIGhlYWRpbmc6ICc8aDE+SGVhZGluZzwvaDE+JyxcbiAgICAgICAgY2hhcHRlclVSTHM6W1xuICAgICAgICAgICAgJzEudHh0JyxcbiAgICAgICAgICAgICcyLnR4dCcsXG4gICAgICAgICAgICAnMy50eHQnXG4gICAgICAgIF1cbiAgICB9LFxuICAgICcxLnR4dCc6ICc8cD5Gb288L3A+JyxcbiAgICAnMi50eHQnOiAnPHA+QmFyPC9wPicsXG4gICAgJzMudHh0JzogJzxwPkJhejwvcD4nXG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKHVybCwgY2FsbGJhY2spe1xuICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oKXtcbiAgICAgICAgaWYoTWF0aC5yYW5kb20oKSAqIDEwIDwgMSl7XG4gICAgICAgICAgICByZXR1cm4gY2FsbGJhY2sobmV3IEVycm9yKCdMb2FkIGZhaWxlZCAtICcgKyB1cmwpKTtcbiAgICAgICAgfVxuICAgICAgICBjYWxsYmFjayhudWxsLCBmaWxlc1t1cmxdKTtcbiAgICB9LCBNYXRoLnJhbmRvbSgpICogNTAwICsgNTAwKTtcbn0iLCJ2YXIgZ2V0SlNPTiA9IHJlcXVpcmUoJy4vZ2V0SlNPTicpO1xudmFyIGFkZEh0bWxUb1BhZ2UgPSByZXF1aXJlKCcuL2FkZEh0bWxUb1BhZ2UnKTtcbnZhciBhZGRUZXh0VG9QYWdlID0gcmVxdWlyZSgnLi9hZGRUZXh0VG9QYWdlJyk7XG5cbi8vIFJpZ2h0byB2ZXJzaW9uIG9mIHRoaXM6IGh0dHA6Ly9qYWtlYXJjaGliYWxkLmNvbS8yMDE0L2VzNy1hc3luYy1mdW5jdGlvbnMvXG5cbnZhciByaWdodG8gPSByZXF1aXJlKCcuLi8nKTtcblxuZnVuY3Rpb24gbG9hZFN0b3J5KCl7XG4gICAgdmFyIGdldFN0b3J5ID0gcmlnaHRvKGdldEpTT04sICdzdG9yeS5qc29uJyksXG4gICAgICAgIGFkZEhlYWRpbmcgPSByaWdodG8uc3luYyhzdG9yeSA9PiBhZGRIdG1sVG9QYWdlKHN0b3J5LmhlYWRpbmcpLCBnZXRTdG9yeSksXG4gICAgICAgIGFkZENoYXB0ZXJzID0gcmlnaHRvLmFsbChyaWdodG8uc3luYyhzdG9yeSA9PlxuICAgICAgICAgICAgc3RvcnkuY2hhcHRlclVSTHMubWFwKGNoYXB0ZXJVcmwgPT4gcmlnaHRvKGdldEpTT04sIGNoYXB0ZXJVcmwpKVxuICAgICAgICAgICAgLnJlZHVjZSgocmVzdWx0LCBnZXRDaGFwdGVyKSA9PiByaWdodG8uc3luYyhhZGRIdG1sVG9QYWdlLCBnZXRDaGFwdGVyLCBbcmVzdWx0XSksIG51bGwpXG4gICAgICAgICwgZ2V0U3RvcnkpKTtcblxuICAgIHJpZ2h0by5hbGwoYWRkSGVhZGluZywgYWRkQ2hhcHRlcnMpKGVycm9yID0+IHtcbiAgICAgICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLnNwaW5uZXInKS5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAgICAgICBlcnJvciA/IGFkZFRleHRUb1BhZ2UoXCJBcmdoLCBicm9rZW46IFwiICsgZXJyb3IubWVzc2FnZSkgOiBhZGRUZXh0VG9QYWdlKCdBbGwgZG9uZScpO1xuICAgIH0pO1xufVxuXG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbG9hZCcsIGxvYWRTdG9yeSk7IiwidmFyIGZvcmVpZ24gPSByZXF1aXJlKCdmb3JlaWduJyksXG4gICAgY3BzZW5pemUgPSByZXF1aXJlKCdjcHNlbml6ZScpO1xuXG5mdW5jdGlvbiBpc1JpZ2h0byh4KXtcbiAgICByZXR1cm4gdHlwZW9mIHggPT09ICdmdW5jdGlvbicgJiYgeC5nZXQgPT09IHg7XG59XG5cbmZ1bmN0aW9uIHNsaWNlKGxpc3QsIHN0YXJ0LCBlbmQpe1xuICAgIHJldHVybiBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChsaXN0LCBzdGFydCwgZW5kKTtcbn1cblxuZnVuY3Rpb24gcmlnaHRvKGZuKXtcbiAgICB2YXIgYXJncyA9IHNsaWNlKGFyZ3VtZW50cywgMSksXG4gICAgICAgIGNvbnRleHQgPSB0aGlzLFxuICAgICAgICBzdGFydGVkID0gMCxcbiAgICAgICAgY2FsbGJhY2tzID0gW10sXG4gICAgICAgIHJlc3VsdHM7XG5cbiAgICBmdW5jdGlvbiBnZXQoY2FsbGJhY2spe1xuICAgICAgICBpZihyZXN1bHRzKXtcbiAgICAgICAgICAgIHJldHVybiBjYWxsYmFjay5hcHBseShjb250ZXh0LCByZXN1bHRzKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNhbGxiYWNrcy5wdXNoKGNhbGxiYWNrKTtcblxuICAgICAgICBpZihzdGFydGVkKyspe1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cblxuICAgICAgICBmb3JlaWduLnBhcmFsbGVsKGZ1bmN0aW9uKHRhc2ssIGRvbmUpe1xuICAgICAgICAgICAgaWYoaXNSaWdodG8odGFzaykpe1xuICAgICAgICAgICAgICAgIHJldHVybiB0YXNrKGZ1bmN0aW9uKGVycm9yKXtcbiAgICAgICAgICAgICAgICAgICAgZG9uZShlcnJvciwgc2xpY2UoYXJndW1lbnRzLCAxKSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmKEFycmF5LmlzQXJyYXkodGFzaykgJiYgaXNSaWdodG8odGFza1swXSkgICYmICFpc1JpZ2h0byh0YXNrWzFdKSl7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRhc2tbMF0oZnVuY3Rpb24oZXJyb3Ipe1xuICAgICAgICAgICAgICAgICAgICB2YXIgYXJncyA9IHNsaWNlKGFyZ3VtZW50cywgMSk7XG4gICAgICAgICAgICAgICAgICAgIGRvbmUoZXJyb3IsIHRhc2suc2xpY2UoMSkubWFwKGZ1bmN0aW9uKGtleSl7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gYXJnc1trZXldO1xuICAgICAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiBkb25lKG51bGwsIHRhc2spO1xuICAgICAgICB9LCBhcmdzLCBmdW5jdGlvbihlcnJvciwgYXJnUmVzdWx0cyl7XG4gICAgICAgICAgICBpZihlcnJvcil7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrcy5mb3JFYWNoKGZ1bmN0aW9uKGNhbGxiYWNrKXtcbiAgICAgICAgICAgICAgICAgICAgY2FsbGJhY2soZXJyb3IpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBhcmdSZXN1bHRzID0gW10uY29uY2F0LmFwcGx5KFtdLCBhcmdSZXN1bHRzKTtcblxuICAgICAgICAgICAgYXJnUmVzdWx0cy5wdXNoKGZ1bmN0aW9uKCl7XG4gICAgICAgICAgICAgICAgcmVzdWx0cyA9IGFyZ3VtZW50cztcbiAgICAgICAgICAgICAgICBjYWxsYmFja3MuZm9yRWFjaChmdW5jdGlvbihjYWxsYmFjayl7XG4gICAgICAgICAgICAgICAgICAgIGNhbGxiYWNrLmFwcGx5KGNvbnRleHQsIHJlc3VsdHMpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIGZuLmFwcGx5KG51bGwsIGFyZ1Jlc3VsdHMpO1xuICAgICAgICB9KTtcbiAgICB9O1xuXG4gICAgZ2V0LmdldCA9IGdldDtcblxuICAgIHJldHVybiBnZXQ7XG59XG5cbnJpZ2h0by5zeW5jID0gZnVuY3Rpb24oZm4pe1xuICAgIHJldHVybiByaWdodG8uYXBwbHkobnVsbCwgW2Nwc2VuaXplKGZuKV0uY29uY2F0KHNsaWNlKGFyZ3VtZW50cywgMSkpKTtcbn07XG5cbnJpZ2h0by5hbGwgPSBmdW5jdGlvbih0YXNrKXtcbiAgICBpZihhcmd1bWVudHMubGVuZ3RoID4gMSl7XG4gICAgICAgIHRhc2sgPSBzbGljZShhcmd1bWVudHMpO1xuICAgIH1cbiAgICBmdW5jdGlvbiByZXNvbHZlKHRhc2tzKXtcbiAgICAgICAgcmV0dXJuIHJpZ2h0by5hcHBseShudWxsLCBbZnVuY3Rpb24oKXtcbiAgICAgICAgICAgIGFyZ3VtZW50c1thcmd1bWVudHMubGVuZ3RoIC0gMV0obnVsbCwgc2xpY2UoYXJndW1lbnRzLCAwLCAtMSkpO1xuICAgICAgICB9XS5jb25jYXQodGFza3MpKTtcbiAgICB9XG5cbiAgICBpZihpc1JpZ2h0byh0YXNrKSl7XG4gICAgICAgIHJldHVybiByaWdodG8oZnVuY3Rpb24odGFza3MsIGRvbmUpe1xuICAgICAgICAgICAgcmVzb2x2ZSh0YXNrcykoZG9uZSk7XG4gICAgICAgIH0sIHRhc2spO1xuICAgIH1cblxuICAgIHJldHVybiByZXNvbHZlKHRhc2spO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSByaWdodG87IiwiXG5mdW5jdGlvbiBjcHNlbml6ZShmbil7XG4gICAgcmV0dXJuIGZ1bmN0aW9uKCl7XG4gICAgICAgIHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKSxcbiAgICAgICAgICAgIGNhbGxiYWNrID0gYXJncy5wb3AoKSxcbiAgICAgICAgICAgIGNvbnRleHQgPSB0aGlzLFxuICAgICAgICAgICAgcmVzdWx0LFxuICAgICAgICAgICAgZXJyb3I7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHJlc3VsdCA9IGZuLmFwcGx5KGNvbnRleHQsIGFyZ3MpO1xuICAgICAgICB9XG4gICAgICAgIGNhdGNoKGV4Y2VwdGlvbil7XG4gICAgICAgICAgICBlcnJvciA9IGV4Y2VwdGlvbjtcbiAgICAgICAgfVxuXG4gICAgICAgIGNhbGxiYWNrKGVycm9yLCByZXN1bHQpO1xuICAgIH07XG59XG5cbm1vZHVsZS5leHBvcnRzID0gY3BzZW5pemU7IiwiZnVuY3Rpb24gcGFyYWxsZWwoZm4sIGl0ZW1zLCBjYWxsYmFjayl7XG4gICAgaWYoIWl0ZW1zIHx8IHR5cGVvZiBpdGVtcyAhPT0gJ29iamVjdCcpe1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0l0ZW1zIG11c3QgYmUgYW4gb2JqZWN0IG9yIGFuIGFycmF5Jyk7XG4gICAgfVxuXG4gICAgdmFyIGtleXMgPSBPYmplY3Qua2V5cyhpdGVtcyksXG4gICAgICAgIGlzQXJyYXkgPSBBcnJheS5pc0FycmF5KGl0ZW1zKSxcbiAgICAgICAgbGVuZ3RoID0gaXNBcnJheSA/IGl0ZW1zLmxlbmd0aCA6IGtleXMubGVuZ3RoLFxuICAgICAgICBmaW5hbFJlc3VsdCA9IG5ldyBpdGVtcy5jb25zdHJ1Y3RvcigpLFxuICAgICAgICBkb25lID0gMCxcbiAgICAgICAgZXJyb3JlZDtcblxuICAgIGlmKGxlbmd0aCA9PT0gMCl7XG4gICAgICAgIHJldHVybiBjYWxsYmFjayhudWxsLCBmaW5hbFJlc3VsdCk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaXNEb25lKGtleSl7XG4gICAgICAgIHJldHVybiBmdW5jdGlvbihlcnJvciwgcmVzdWx0KXtcblxuICAgICAgICAgICAgaWYoZXJyb3JlZCl7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZihlcnJvcil7XG4gICAgICAgICAgICAgICAgZXJyb3JlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrKGVycm9yKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgZmluYWxSZXN1bHRba2V5XSA9IGFyZ3VtZW50cy5sZW5ndGggPiAyID8gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzLCAxKSA6IHJlc3VsdDtcblxuICAgICAgICAgICAgaWYoKytkb25lID09PSBsZW5ndGgpe1xuICAgICAgICAgICAgICAgIGNhbGxiYWNrKG51bGwsIGZpbmFsUmVzdWx0KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbmd0aDsgaSsrKSB7XG4gICAgICAgIHZhciBrZXkgPSBrZXlzW2ldO1xuICAgICAgICBpZihpc0FycmF5ICYmIGlzTmFOKGtleSkpe1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICBmbihpdGVtc1trZXldLCBpc0RvbmUoa2V5KSk7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBzZXJpZXMoZm4sIGl0ZW1zLCBjYWxsYmFjayl7XG4gICAgaWYoIWl0ZW1zIHx8IHR5cGVvZiBpdGVtcyAhPT0gJ29iamVjdCcpe1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0l0ZW1zIG11c3QgYmUgYW4gb2JqZWN0IG9yIGFuIGFycmF5Jyk7XG4gICAgfVxuXG4gICAgdmFyIGtleXMgPSBPYmplY3Qua2V5cyhpdGVtcyksXG4gICAgICAgIGlzQXJyYXkgPSBBcnJheS5pc0FycmF5KGl0ZW1zKSxcbiAgICAgICAgbGVuZ3RoID0gaXNBcnJheSA/IGl0ZW1zLmxlbmd0aCA6IGtleXMubGVuZ3RoLFxuICAgICAgICBmaW5hbFJlc3VsdCA9IG5ldyBpdGVtcy5jb25zdHJ1Y3RvcigpO1xuXG4gICAgaWYobGVuZ3RoID09PSAwKXtcbiAgICAgICAgcmV0dXJuIGNhbGxiYWNrKG51bGwsIGZpbmFsUmVzdWx0KTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBuZXh0KGluZGV4KXtcbiAgICAgICAgdmFyIGtleSA9IGtleXNbaW5kZXhdO1xuXG4gICAgICAgIGluZGV4Kys7XG5cbiAgICAgICAgaWYoaXNBcnJheSAmJiBpc05hTihrZXkpKXtcbiAgICAgICAgICAgIHJldHVybiBuZXh0KGluZGV4KTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZuKGl0ZW1zW2tleXNba2V5XV0sIGZ1bmN0aW9uIChlcnJvciwgcmVzdWx0KSB7XG4gICAgICAgICAgICBpZihlcnJvcil7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrKGVycm9yKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgZmluYWxSZXN1bHRba2V5XSA9IGFyZ3VtZW50cy5sZW5ndGggPiAyID8gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzLCAxKSA6IHJlc3VsdDtcblxuICAgICAgICAgICAgaWYoaW5kZXggPT09IGxlbmd0aCl7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrKG51bGwsIGZpbmFsUmVzdWx0KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgbmV4dChpbmRleCk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIG5leHQoMCk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIHBhcmFsbGVsOiBwYXJhbGxlbCxcbiAgICBzZXJpZXM6IHNlcmllc1xufTsiXX0= +},{}]},{},[4]); diff --git a/example/asyncAwaitCompare/index.js b/example/asyncAwaitCompare/index.js index 0fa6a35..418ba14 100644 --- a/example/asyncAwaitCompare/index.js +++ b/example/asyncAwaitCompare/index.js @@ -4,19 +4,22 @@ var addTextToPage = require('./addTextToPage'); // Righto version of this: http://jakearchibald.com/2014/es7-async-functions/ -var righto = require('../'); +var righto = require('../../'); function loadStory(){ - var getStory = righto(getJSON, 'story.json'), - addHeading = righto.sync(story => addHtmlToPage(story.heading), getStory), - addChapters = righto.all(righto.sync(story => - story.chapterURLs.map(chapterUrl => righto(getJSON, chapterUrl)) - .reduce((result, getChapter) => righto.sync(addHtmlToPage, getChapter, [result]), null) - , getStory)); + var story = righto(getJSON, 'story.json'); - righto.all(addHeading, addChapters)(error => { + var headingAdded = story.get(story => addHtmlToPage(story.heading)); + + var chaptersAdded = righto.reduce(story.get(story => story.chapterURLs.map(chapterUrl => + righto(getJSON, chapterUrl)().get(addHtmlToPage) + ))); + + righto.reduce([headingAdded, chaptersAdded])(error => { + error ? + addTextToPage("Argh, broken: " + error.message) : + addTextToPage('All done'); document.querySelector('.spinner').style.display = 'none'; - error ? addTextToPage("Argh, broken: " + error.message) : addTextToPage('All done'); }); } diff --git a/example/helloWorld/index.js b/example/helloWorld/index.js index b474ec0..da09715 100644 --- a/example/helloWorld/index.js +++ b/example/helloWorld/index.js @@ -34,11 +34,11 @@ function createNewUser(userData, callback){ var user = righto(createUser, userData, account); // righto -> error, user. - var pets = righto.all(userData.pets.map(function(petData){ // righto -> error, [pet...]. - return righto(createPet, user.get('id'), petData); - })); + var pets = righto.all(userData.pets.map(petData => + righto(createPet, user.get('id'), petData); + )); - var done = righto.mate(user, [pets]); // righto -> error, account. (IGNORED [pet...]) + var done = righto.mate(user, righto.after(pets)); // righto -> error, account. (IGNORED [pet...]) done(callback); } \ No newline at end of file diff --git a/index.js b/index.js index 003499b..080641b 100644 --- a/index.js +++ b/index.js @@ -327,6 +327,8 @@ function resolver(callback){ }); defer(resolveDependencies.bind(null, context.args, complete, resolveDependency.bind(this.resolve))); + + return this.resolve; }; function righto(){ @@ -388,25 +390,29 @@ righto.all = function(value){ }; righto.reduce = function(values, reducer, seed){ - if(!values || !values.reduce){ - throw new Error('values was not a reduceable object (like an array)'); - } + var hasSeed = arguments.length >= 3; - values = values.slice(); + return righto.from(values).get(function(values){ + if(!values || !values.reduce){ + throw new Error('values was not a reduceable object (like an array)'); + } - if(arguments.length < 3){ - seed = righto(values.shift()); - } + values = values.slice(); - return values.reduce(function(previous, next){ - if(reducer){ - return righto(function(previous, done){ - reducer(previous, next)(done); - }, previous); + if(!hasSeed){ + seed = righto(values.shift()); } - return righto(done => next(done), righto.after(righto.from(previous))); - }, seed); + return values.reduce(function(previous, next){ + if(reducer){ + return righto(function(previous, done){ + reducer(previous, next)(done); + }, previous); + } + + return righto(done => next(done), righto.after(righto.from(previous))); + }, seed); + }); }; righto.from = function(value){ diff --git a/package.json b/package.json index 0381dbf..75b2ff0 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,7 @@ }, "scripts": { "test": "node test", - "watchtest": "watchify -d test/index.js -o test/index.browser.js", - "watchexample": "linklocal link; watchify -d example/index.js -o example/index.browser.js" + "watchtest": "watchify -d test/index.js -o test/index.browser.js" }, "author": "", "license": "ISC", diff --git a/test/index.js b/test/index.js index 5792221..6ee506a 100644 --- a/test/index.js +++ b/test/index.js @@ -1423,4 +1423,26 @@ test('righto.fail resolvable', function(t){ falure(function(error){ t.equal(error, 'reasons'); }); +}); + +test('righto prerun return', function(t){ + t.plan(2); + + + var start = Date.now(); + var lazyRun = righto(function(done){ + setTimeout(done, 100, null, true); + }), + eagerRun = righto(function(done){ + setTimeout(done, 100, null, true); + })(); // call immediately so that it eagerly runs. + + setTimeout(function(){ + lazyRun(function(){ + t.ok(Date.now() - start >= 150, 'Result completed in at least 150ms'); + }); + eagerRun(function(){ + t.ok(Date.now() - start < 125, 'Result completed in significantly less than 150ms'); + }); + }, 50); }); \ No newline at end of file