From 9961afa3dd08d0cfaa66c4acac01e35b72b99b80 Mon Sep 17 00:00:00 2001 From: harttle Date: Mon, 31 Oct 2016 23:45:41 +0800 Subject: [PATCH] feture: promise.mapSeries --- index.js | 4 ++-- src/util/promise.js | 26 +++++++++++++++++----- test/util/promise.js | 51 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 69 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 844936119..a851d78f0 100644 --- a/index.js +++ b/index.js @@ -13,7 +13,7 @@ const Syntax = require('./src/syntax.js'); const tags = require('./tags'); const filters = require('./filters'); const Promise = require('any-promise'); -const someSeries = require('./src/util/promise.js').someSeries; +const anySeries = require('./src/util/promise.js').anySeries; var _engine = { init: function(tag, filter, options) { @@ -78,7 +78,7 @@ var _engine = { }, lookup: function(filepath) { var paths = this.options.root.map(root => pathResolve(root, filepath)); - return someSeries(paths, path => statFileAsync(path).then(() => path)); + return anySeries(paths, path => statFileAsync(path).then(() => path)); }, getTemplate: function(filepath) { if (!filepath.match(/\.\w+$/)) { diff --git a/src/util/promise.js b/src/util/promise.js index 03c8b69b7..708407b8d 100644 --- a/src/util/promise.js +++ b/src/util/promise.js @@ -6,14 +6,30 @@ const Promise = require('any-promise'); * @param {Array} iteratee returns a new promise. * The iteratee is invoked with three arguments: (value, index, iterable). */ -function someSeries(iterable, iteratee) { +function anySeries(iterable, iteratee) { var ret = Promise.reject(new Error('init')); iterable.forEach(function(item, idx) { - ret = ret - .then(x => x) - .catch(e => iteratee(item, idx, iterable)); + ret = ret.catch(e => iteratee(item, idx, iterable)); }); return ret; } -exports.someSeries = someSeries; +/* + * Call functions in serial until someone rejected. + * @param {Array} iterable the array to iterate with. + * @param {Array} iteratee returns a new promise. + * The iteratee is invoked with three arguments: (value, index, iterable). + */ +function mapSeries(iterable, iteratee) { + var ret = Promise.resolve('init'); + var result = []; + iterable.forEach(function(item, idx) { + ret = ret + .then(() => iteratee(item, idx, iterable)) + .then(x => result.push(x)); + }); + return ret.then(() => result); +} + +exports.anySeries = anySeries; +exports.mapSeries = mapSeries; diff --git a/test/util/promise.js b/test/util/promise.js index 47d8c8a2c..714dffaa2 100644 --- a/test/util/promise.js +++ b/test/util/promise.js @@ -7,12 +7,12 @@ chai.use(require("sinon-chai")); var P = require('../../src/util/promise.js'); describe('util/promise', function() { - describe('.someSeries()', function() { + describe('.anySeries()', function() { it('should resolve in series', function() { var spy1 = sinon.spy(), spy2 = sinon.spy(); return P - .someSeries( + .anySeries( ['first', 'second'], (item, idx) => new Promise(function(resolve, reject) { if (idx === 0) { @@ -28,18 +28,18 @@ describe('util/promise', function() { .then(() => expect(spy2).to.have.been.calledAfter(spy1)); }); it('should reject when all rejected', function() { - var p = P.someSeries(['first', 'second', 'third'], + var p = P.anySeries(['first', 'second', 'third'], item => Promise.reject(new Error(item))); return expect(p).to.be.rejectedWith("third"); }); it('should resolve the value that first callback resolved', () => { - var p = P.someSeries(['first', 'second'], + var p = P.anySeries(['first', 'second'], item => Promise.resolve(item)); return expect(p).to.eventually.equal('first'); }); it('should not call rest of callbacks once resolved', () => { var spy = sinon.spy(); - return P.someSeries(['first', 'second'], (item, idx) => { + return P.anySeries(['first', 'second'], (item, idx) => { if (idx > 0) { spy(); } @@ -48,4 +48,45 @@ describe('util/promise', function() { .then(() => expect(spy).to.not.have.been.called); }); }); + describe('.mapSeries()', function() { + it('should resolve when all resolved', function() { + var p = P.mapSeries(['first', 'second', 'third'], + item => Promise.resolve(item)); + return expect(p).to.eventually.deep.equal(['first', 'second', "third"]); + }); + it('should reject with the error that first callback rejected', () => { + var p = P.mapSeries(['first', 'second'], + item => Promise.reject(item)); + return expect(p).to.rejectedWith('first'); + }); + it('should resolve in series', function() { + var spy1 = sinon.spy(), + spy2 = sinon.spy(); + return P + .mapSeries( + ['first', 'second'], + (item, idx) => new Promise(function(resolve, reject) { + if (idx === 0) { + setTimeout(function() { + spy1(); + resolve('first cb'); + }, 10); + } else { + spy2(); + resolve('foo'); + } + })) + .then(() => expect(spy2).to.have.been.calledAfter(spy1)); + }); + it('should not call rest of callbacks once rejected', () => { + var spy = sinon.spy(); + return P.mapSeries(['first', 'second'], (item, idx) => { + if (idx > 0) { + spy(); + } + return Promise.reject(new Error(item)); + }) + .catch(() => expect(spy).to.not.have.been.called); + }); + }); });