├── .gitignore ├── index.js ├── package.json ├── LICENSE ├── README.md └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # yarn 40 | yarn.lock 41 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = function (promises) { 3 | 4 | if (!Array.isArray(promises)) { 5 | throw new Error('First argument need to be an array of Promises'); 6 | } 7 | 8 | let count = 0; 9 | let results = []; 10 | 11 | const iterateeFunc = (previousPromise, currentPromise) => { 12 | return previousPromise 13 | .then(function (result) { 14 | if (count++ !== 0) results.push(result); 15 | return currentPromise(result, results, count); 16 | }) 17 | } 18 | 19 | return promises 20 | // this call allows the last promises's resolved result to be obtained cleanly 21 | .concat(() => Promise.resolve()) 22 | // reduce() concatenates the promises. E.g. p1.then(p2).then(p3) 23 | .reduce(iterateeFunc, Promise.resolve(false)) 24 | .then(() => results) 25 | }; 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "promise-sequential", 3 | "version": "1.1.1", 4 | "description": "Simple like Promise.all(), but sequentially!", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/mocha test --timeout 15000" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/russiann/promise-sequential.git" 12 | }, 13 | "keywords": [ 14 | "promise", 15 | "queue", 16 | "seq", 17 | "sequence", 18 | "sequential" 19 | ], 20 | "author": "Russian Rebouças", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/russiann/promise-sequential/issues" 24 | }, 25 | "homepage": "https://github.com/russiann/promise-sequential#readme", 26 | "devDependencies": { 27 | "chai": "^3.5.0", 28 | "mocha": "^3.0.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Russian Rebouças 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # promise-sequential 2 | Simple like Promise.all(), but sequentially! 3 | 4 | [![NPM version](https://badge.fury.io/js/promise-sequential.png)](http://badge.fury.io/js/promise-sequential) 5 | 6 | [![Npm Downloads](https://nodei.co/npm/promise-sequential.png?downloads=true&stars=true)](https://nodei.co/npm/promise-sequential.png?downloads=true&stars=true) 7 | 8 | 9 | ## Installation 10 | 11 | ``` 12 | npm install --save promise-sequential 13 | ``` 14 | ## Usage 15 | 16 | ```js 17 | const sequential = require('promise-sequential'); 18 | 19 | const items = [ 20 | () => new Promise( ... ) 21 | () => new Promise( ... ) 22 | () => new Promise( ... ) 23 | ]; 24 | 25 | sequential(items) 26 | .then(res => { 27 | // ... 28 | }) 29 | .catch(err => { 30 | // ... 31 | }) 32 | ``` 33 | 34 | There is only one difference between Promise.all usage and promise-sequential usage: promise-sequential receive an Array of functions that each returns a promise. 35 | 36 | Each function brings three params: 37 | 38 | | Name | Description | 39 | | ----------------- | ------------------------------------------------------------ | 40 | | previousResponse | The response of previous iteration | 41 | | responses | All responses received at the time | 42 | | count | The current count. | 43 | 44 | ### More complex usage 45 | 46 | ```js 47 | 48 | const sequential = require('promise-sequential'); 49 | const array = [1,2,3,4,5]; 50 | 51 | sequential(array.map((item) => { 52 | return function(previousResponse, responses, count) { 53 | 54 | return new Promise(resolve => { 55 | setTimeout(() => { 56 | resolve(item) 57 | }, 1000) 58 | }) 59 | 60 | } 61 | })) 62 | .then(res => { 63 | // ... 64 | }) 65 | .catch(err => { 66 | // ... 67 | }) 68 | ``` 69 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const expect = require("chai").expect; 2 | const promiseq = require('../index.js'); 3 | 4 | describe('sequentially promises', () => { 5 | 6 | it('should throw a error if not pass an array of promises', () => { 7 | const func = () => { 8 | promiseq({}); 9 | } 10 | expect(func).to.throw('First argument need to be an array of Promises'); 11 | }); 12 | 13 | it('should return an Array containing all promises resolved results as response', (done) => { 14 | 15 | const vals = [1, 2, 3] 16 | 17 | let promises = vals.map((item) => { 18 | 19 | return function (previousResponse, results, count) { 20 | return new Promise(resolve => { 21 | setTimeout(function () { 22 | resolve(item) 23 | }, 1000) 24 | }); 25 | } 26 | 27 | }); 28 | promiseq(promises) 29 | .then(res => { 30 | expect(res).to.be.instanceof(Array); 31 | expect(res).to.eql(vals); 32 | done(); 33 | }) 34 | }); 35 | 36 | it('should be able to catch value passed on reject of a promise', (done) => { 37 | 38 | let promises = [1,2].map((item) => { 39 | return function (previousResponse) { 40 | return new Promise((r, reject) => { 41 | setTimeout(() => { 42 | reject(1) 43 | }, 1000) 44 | }) 45 | } 46 | }); 47 | 48 | promiseq(promises) 49 | .catch(err => { 50 | expect(err).to.equal(1); 51 | done(); 52 | }) 53 | }); 54 | 55 | it('should handle case where input array is empty', (done) => { 56 | 57 | const expectedResult = []; 58 | promiseq([]) 59 | .then((res) => { 60 | expect(res).to.deep.equal(expectedResult); 61 | done(); 62 | }); 63 | }); 64 | 65 | it('should not keep running after one of the promises is rejected', (done) => { 66 | /* 67 | This test checks whether promise-sequential keeps executing promises after one of them 68 | was rejected. 69 | 70 | In order to test the assertions each promise will update the value of a key in the 71 | 'resolvedPromises' object. The expectation is that the 'resolvedPromises' values should 72 | only be updated up until the last successful promise. E.g. 73 | [ () => p1, // updates 'resolvedPromises.p1' 74 | () => rejectedPromise, // should not update 'resolvedPromises.p2' 75 | () => p3 ] // should not update 'resolvedPromises.p3' 76 | */ 77 | const resolvedPromises = { p1: false, p2: false, p3: false }; 78 | const expectedResolvedPromises = { p1: true, p2: false, p3: false }; 79 | 80 | function updateResolvedPromises(key) { 81 | return new Promise((resolve) => { 82 | resolvedPromises[key] = true; 83 | resolve(); 84 | }); 85 | } 86 | 87 | const promises = [ 88 | () => updateResolvedPromises('p1'), 89 | 90 | () => Promise.reject(), 91 | 92 | () => updateResolvedPromises('p3'), 93 | ]; 94 | 95 | promiseq(promises) 96 | .catch(() => {}) 97 | .then(() => { 98 | expect(resolvedPromises).to.deep.equal(expectedResolvedPromises); 99 | done(); 100 | }) 101 | }); 102 | 103 | }) 104 | --------------------------------------------------------------------------------