From d6f4c652f4db808d9552e1e8fe19002077ed5532 Mon Sep 17 00:00:00 2001 From: zoubin Date: Thu, 10 Dec 2015 15:11:12 +0800 Subject: [PATCH] Support series --- README.md | 250 ++++++++++++++++++++++++++++---------------- example/dynamic.js | 5 +- example/parallel.js | 8 +- example/sequence.js | 24 +---- example/series.js | 41 ++++++++ example/thunkify.js | 32 +++--- index.js | 1 + lib/runner.js | 15 +++ test/chain.js | 11 ++ test/lib/delay.js | 11 ++ test/series.js | 98 +++++++++++++++++ 11 files changed, 363 insertions(+), 133 deletions(-) create mode 100644 example/series.js create mode 100644 test/lib/delay.js create mode 100644 test/series.js diff --git a/README.md b/README.md index 095b3f8..3dcb8af 100644 --- a/README.md +++ b/README.md @@ -56,77 +56,158 @@ function stream() { ## API -### cb = thunkify(...tasks) -Return a callback to run the specified tasks in the appearance order. +```javascript +var Runner = require('./lib/runner') +var runner = new Runner({ input: false, output: false }) + +exports = module.exports = runner.thunkify.bind(runner) +exports.run = exports.sequence = runner.sequence.bind(runner) +exports.parallel = runner.parallel.bind(runner) +exports.series = runner.series.bind(runner) +exports.Runner = Runner + +``` -`cb` will return a promise. +### Runner +`var runner = Runner(opts)` +Create a new runner instance. + +#### opts + +##### input +Specify whether to pass the results of the previous callback to the next as arguments. + +Type: `Boolean` + +Default: `true` ```javascript -var res = [] -thunkify( - function () { - res.push(1) +var Runner = require('callback-sequence').Runner + +var runner = Runner({ input: true }) + +runner.thunkify( + function (a, b) { + // 3 + return a + b }, - function (next) { + function (sum, next) { process.nextTick(function () { - res.push(2) - next() + // 6 + next(null, sum * 2) }) }, - function () { + function (product) { return Promise.resolve().then(function () { - res.push(3) + // 7 + return product + 1 }) } -)() -.then(function () { - // [1, 2, 3] +)(1, 2) +.then(function (res) { + // [7] console.log(res) }) + ``` -### thunkify.run(tasks) -It just runs `tasks` like you call the function returned by `thunkify` +##### output +Specify whether to deliver results. + +Type: `Boolean` + +Default: `true` + +If `false`, the final results will always be `[]`. + +##### run +Specify a runner function to run each callback. + +Type: `Function`, `Object` + +Default: `null` + +If `Function`, it receives a callback followed by a list of arguments, +and should return a promise to fetch the results (`Array`). + +If `Object`, it is passed to +[`Runner of run-callback`](https://github.com/zoubin/run-callback#runner--runrunner) +to create a runner function. -**NOTE**: if some task is an array of sub-tasks, they will be run in parallel. + +#### cb = Runner.prototype.thunkify(...tasks) +Return a callback to run the specified tasks in the appearance order. ```javascript -var run = require('callback-sequence').run +var runner = Runner() -var res = [] -run([ - function () { res.push(1) }, +runner.thunkify( + function (res) { + return res + 1 + }, + function (res) { + return Promise.resolve() + .then(function () { + return res + 1 + }) + }, + function (res, next) { + process.nextTick(function () { + next(null, res - 1, res + 1) + }) + } +)(1) +.then(function (res) { + // [ 2, 4 ] + console.log(res) +}) + +``` + +#### Runner.prototype.sequence(tasks) +Run `tasks` in sequence. + +**NOTE**: directly nested array of tasks will be run with `Runner.prototype.parallel`. + +```javascript +var runner = Runner() + +runner.sequence([ + function () { console.log(1) }, [ function (cb) { setTimeout(function() { - res.push(3) + console.log(3) cb() }, 0) }, function () { return new Promise(function (resolve) { process.nextTick(function () { - res.push(2) + console.log(2) resolve() }) }) }, ], - function () { res.push(4) }, -] -) -.then(function () { - // [1, 2, 3, 4] - console.log(res) + function () { console.log(4) }, +]).then(function () { + console.log('DONE') }) +// 1 +// 2 +// 3 +// 4 +// DONE + ``` -Callbacks an be added dynamically: +Callbacks can be added dynamically: ```javascript -var run = require('callback-sequence').run +var runner = Runner() var count = 5 var tasks = [] @@ -141,7 +222,7 @@ function task(next) { next() }) } -run(tasks).then(function () { +runner.sequence(tasks).then(function () { // [5, 4, 3, 2, 1] console.log(res) }) @@ -150,16 +231,16 @@ tasks.push(task) ``` -### thunkify.parallel(tasks) +#### Runner.prototype.parallel(tasks) Run the specified tasks in parallel. -**NOTE**: if some task is an array of sub-tasks, they will be run in sequence. +**NOTE**: directly nested array of tasks will be run with `Runner.prototype.sequence`. ```javascript -var parallel = require('callback-sequence').parallel +var runner = Runner() var res = [] -parallel([ +runner.parallel([ function () { res.push(1) }, [ function () { @@ -179,8 +260,7 @@ parallel([ res.push(2) cb() }, -] -) +]) .then(function () { // [1, 2, 4, 5, 3] console.log(res) @@ -188,73 +268,59 @@ parallel([ ``` -### Runner = thunkify.Runner(opts) -Return a new runner instance, with the following methods: +#### Runner.prototype.series(...tasks) +Run `tasks` in sequence. -* `sequence`: just like `thunkify.run` -* `parallel`: just like `thunkify.parallel` -* `thunkify`: just like `thunkify` +However, while the results of `sequence` is that of the last task, +the results of `series` is an array containing all results of the tasks. -#### opts +In addition, the results of the previous task will not be passed to the next as arguments. -##### input -Specify whether to pass the results of the previous callback to the next as arguments. - -Type: `Boolean` - -Default: `true` +**NOTE**: each element will be passed to `Runner.prototype.sequence`. ```javascript -var Runner = require('callback-sequence').Runner +var runner = Runner() -var runner = Runner({ input: true }) - -runner.thunkify( - function (a, b) { - // 3 - return a + b - }, - function (sum, next) { - process.nextTick(function () { - // 6 - next(null, sum * 2) - }) +runner.series( + function () { + console.log(1) + return 1 }, - function (product) { - return Promise.resolve().then(function () { - // 7 - return product + 1 - }) + [ + function () { + return Promise.resolve().then(function () { + console.log(4) + return 4 + }) + }, + function (cb) { + setTimeout(function() { + console.log(3) + cb(null, 3) + }, 0) + }, + function () { + console.log(5) + return 5 + }, + ], + function (cb) { + console.log(2) + cb(null, 2) } -)(1, 2) +) .then(function (res) { - // [7] + // 1 + // 5 + // 4 + // 3 + // 2 + // [ [ 1 ], [ [ 4 ], [ 3 ], [ 5 ] ], [ 2 ] ] console.log(res) }) - ``` -##### output -Specify whether to pass the results of the last callback to the final results. - -Type: `Boolean` - -Default: `true` - -##### run -Specify a runner function to run each callback. - -Type: `Function`, `Object` - -Default: `null` - -If `Function`, it receives a callback followed by a list of arguments, -and should return a promise to fetch the results (`Array`). - -If `Object`, it is passed to -[`Runner of run-callback`](https://github.com/zoubin/run-callback#runner--runrunner) -to create a runner function. ## [Changelog](changelog.md) diff --git a/example/dynamic.js b/example/dynamic.js index 873ed0b..26ada2f 100644 --- a/example/dynamic.js +++ b/example/dynamic.js @@ -1,4 +1,5 @@ -var run = require('..').run +var Runner = require('..').Runner +var runner = Runner() var count = 5 var tasks = [] @@ -13,7 +14,7 @@ function task(next) { next() }) } -run(tasks).then(function () { +runner.sequence(tasks).then(function () { // [5, 4, 3, 2, 1] console.log(res) }) diff --git a/example/parallel.js b/example/parallel.js index 68f1f85..3317371 100644 --- a/example/parallel.js +++ b/example/parallel.js @@ -1,7 +1,8 @@ -var parallel = require('..').parallel +var Runner = require('..').Runner +var runner = Runner() var res = [] -parallel([ +runner.parallel([ function () { res.push(1) }, [ function () { @@ -21,8 +22,7 @@ parallel([ res.push(2) cb() }, -] -) +]) .then(function () { // [1, 2, 4, 5, 3] console.log(res) diff --git a/example/sequence.js b/example/sequence.js index 1c9bdca..230235d 100644 --- a/example/sequence.js +++ b/example/sequence.js @@ -1,6 +1,7 @@ -var sequence = require('..') +var Runner = require('..').Runner +var runner = Runner() -sequence( +runner.sequence([ function () { console.log(1) }, [ function (cb) { @@ -18,23 +19,8 @@ sequence( }) }, ], - function () { - var Readable = require('stream').Readable - var rs = Readable({ objectMode: true }) - var data = [null, 'a', 'b'] - rs._read = function () { - this.push(data.pop()) - } - process.nextTick(function () { - rs.on('data', function (d) { - if (d) { - console.log(d) - } - }) - }) - return rs - } -)().then(function () { + function () { console.log(4) }, +]).then(function () { console.log('DONE') }) diff --git a/example/series.js b/example/series.js new file mode 100644 index 0000000..705de5a --- /dev/null +++ b/example/series.js @@ -0,0 +1,41 @@ +var Runner = require('..').Runner +var runner = Runner() + +runner.series( + function () { + console.log(1) + return 1 + }, + [ + function () { + return Promise.resolve().then(function () { + console.log(4) + return 4 + }) + }, + function (cb) { + setTimeout(function() { + console.log(3) + cb(null, 3) + }, 0) + }, + function () { + console.log(5) + return 5 + }, + ], + function (cb) { + console.log(2) + cb(null, 2) + } +) +.then(function (res) { + // 1 + // 5 + // 4 + // 3 + // 2 + // [ [ 1 ], [ [ 4 ], [ 3 ], [ 5 ] ], [ 2 ] ] + console.log(res) +}) + diff --git a/example/thunkify.js b/example/thunkify.js index d492f2d..0809b29 100644 --- a/example/thunkify.js +++ b/example/thunkify.js @@ -1,23 +1,23 @@ -var thunkify = require('..') +var Runner = require('..').Runner +var runner = Runner() -var res = [] -thunkify( - function () { - res.push(1) +runner.thunkify( + function (res) { + return res + 1 }, - function (next) { - process.nextTick(function () { - res.push(2) - next() - }) + function (res) { + return Promise.resolve() + .then(function () { + return res + 1 + }) }, - function () { - return Promise.resolve().then(function () { - res.push(3) + function (res, next) { + process.nextTick(function () { + next(null, res - 1, res + 1) }) } -)() -.then(function () { - // [1, 2, 3] +)(1) +.then(function (res) { + // [ 2, 4 ] console.log(res) }) diff --git a/index.js b/index.js index c0cc9ab..92d797d 100644 --- a/index.js +++ b/index.js @@ -4,5 +4,6 @@ var runner = new Runner({ input: false, output: false }) exports = module.exports = runner.thunkify.bind(runner) exports.run = exports.sequence = runner.sequence.bind(runner) exports.parallel = runner.parallel.bind(runner) +exports.series = runner.series.bind(runner) exports.Runner = Runner diff --git a/lib/runner.js b/lib/runner.js index 3ce4621..84dda38 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -65,6 +65,21 @@ Runner.prototype.sequence = function(cbs, args, i) { }) } +Runner.prototype.series = function() { + var cbs = slice(arguments) + var self = this + return cbs.reduce(function (last, cb) { + return Promise.resolve(last) + .then(function (results) { + return Promise.resolve(self.sequence([cb])) + .then(function (res) { + results.push(res) + return self.normalizeResult(results) + }) + }) + }, Promise.resolve([])) +} + Runner.prototype.parallel = function(cbs, args) { if (!Array.isArray(cbs)) cbs = [cbs] args = this.normalizeArgs(args) diff --git a/test/chain.js b/test/chain.js index 38ceeca..72f12d9 100644 --- a/test/chain.js +++ b/test/chain.js @@ -31,6 +31,17 @@ test('sync', function(t) { }, ]) }) + .then(function () { + return run([ + function () { + return [1] + }, + function (a, next) { + t.same(a, [1]) + next() + }, + ]) + }) }) test('async', function(t) { diff --git a/test/lib/delay.js b/test/lib/delay.js new file mode 100644 index 0000000..020b33a --- /dev/null +++ b/test/lib/delay.js @@ -0,0 +1,11 @@ + +module.exports = function (res, t, n) { + n = n == null ? t : n + return function (cb) { + setTimeout(function() { + res.push(n) + cb() + }, t) + } +} + diff --git a/test/series.js b/test/series.js new file mode 100644 index 0000000..ba34801 --- /dev/null +++ b/test/series.js @@ -0,0 +1,98 @@ +var test = require('tap').test +var api = require('..') +var Runner = api.Runner +var delay = require('./lib/delay') + +test('order', function(t) { + t.plan(1) + + var res = [] + var runner = Runner() + runner.series( + delay(res, 10, 1), + [ + delay(res, 0, 2), + delay(res, 20, 3), + delay(res, 10, 4), + ], + delay(res, 0, 5) + ) + .then(function () { + t.same(res, [1, 2, 4, 3, 5]) + }) +}) + +test('results', function(t) { + t.plan(2) + + var runner = Runner() + runner.series( + function (next) { + next(null, 1, 2) + }, + function () { + return 3 + }, + function () { + return Promise.resolve() + } + ) + .then(function (res) { + t.same(res, [[1, 2], [3], []]) + }) + + api.series( + function (next) { + next(null, 1, 2) + }, + function () { + return 3 + }, + function () { + return Promise.resolve() + } + ) + .then(function (res) { + t.same(res, []) + }) +}) + +test('args', function(t) { + t.plan(1) + var runner = Runner() + + runner.series( + function (next) { + next(null, 1) + }, + [ + function () { + return Promise.resolve(2) + }, + [ + function (next) { + next(null, 3) + }, + function (a) { + return Promise.resolve(a + 1) + }, + function (a, next) { + next(null, a + 1, a + 2) + }, + ], + function (next) { + next(null, 4) + }, + ], + function () { + } + ) + .then(function (res) { + t.same(res, [ + [1], + [[2], [5, 6], [4]], + [], + ]) + }) +}) +