├── .gitignore ├── prottle.d.ts ├── .editorconfig ├── .github └── workflows │ └── build.yml ├── package.json ├── prottle.js ├── README.md └── prottle.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /prottle.d.ts: -------------------------------------------------------------------------------- 1 | declare const prottle: ( 2 | limit: number, 3 | arr: Array<() => any> 4 | ) => Promise; 5 | 6 | export = prottle; 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | 7 | indent_style = space 8 | indent_size = 2 9 | 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-node@v2 9 | with: 10 | node-version: 14.x 11 | check-latest: true 12 | - run: npm install 13 | - run: npm test 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prottle", 3 | "version": "1.0.6", 4 | "description": "Promise.all() throttle", 5 | "main": "prottle.js", 6 | "typings": "prottle.d.ts", 7 | "scripts": { 8 | "test": "mocha ./*.test.js" 9 | }, 10 | "author": "Iskren Slavov ", 11 | "license": "BSD-2-Clause", 12 | "devDependencies": { 13 | "chai": "^4.3.4", 14 | "chai-as-promised": "^7.1.1", 15 | "mocha": "^8.3.2" 16 | }, 17 | "files": [ 18 | "prottle.js", 19 | "prottle.d.ts" 20 | ], 21 | "keywords": [ 22 | "promise", 23 | "throttle", 24 | "limit", 25 | "promise.all", 26 | "promises", 27 | "browserify" 28 | ], 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/zewish/prottle.git" 32 | }, 33 | "bugs": { 34 | "url": "https://github.com/zewish/prottle/issues" 35 | }, 36 | "homepage": "https://github.com/zewish/prottle" 37 | } 38 | -------------------------------------------------------------------------------- /prottle.js: -------------------------------------------------------------------------------- 1 | const EventEmitter = require('events'); 2 | 3 | module.exports = (limit, arr) => { 4 | limit = parseInt(limit); 5 | 6 | if (isNaN(limit) || limit <= 0) { 7 | return Promise.reject(Error('Limit must be at least 1')); 8 | } 9 | 10 | if (!Array.isArray(arr)) { 11 | return Promise.reject(Error('Array of promises required')); 12 | } 13 | 14 | const promises = [].concat(arr); 15 | const results = []; 16 | const emitter = new EventEmitter(); 17 | 18 | const run = (batch, id) => { 19 | Promise.all(batch.map( 20 | (fn) => Promise.resolve(fn()) 21 | )) 22 | .then((res) => { 23 | results.push.apply(results, res); 24 | emitter.emit(`end:${id}`); 25 | }) 26 | .catch((err) => { 27 | emitter.emit('error', err); 28 | }); 29 | }; 30 | 31 | let add = (batch, id) => { 32 | if (id === 0) { 33 | return run(batch, id); 34 | } 35 | 36 | emitter.once(`end:${id-1}`, () => run(batch, id)); 37 | }; 38 | 39 | let idx = -1; 40 | while (promises.length) { 41 | add( 42 | promises.splice(0, limit), 43 | ++idx 44 | ); 45 | } 46 | 47 | return new Promise((resolve, reject) => { 48 | emitter 49 | .once(`end:${idx}`, () => { 50 | resolve(results); 51 | }) 52 | .once('error', err => { 53 | emitter.removeAllListeners(); 54 | reject(err); 55 | }); 56 | }); 57 | }; 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![NPM version](https://img.shields.io/npm/v/prottle.svg?style=flat-square)](https://www.npmjs.com/package/prottle) 2 | [![Build Status](https://github.com/zewish/prottle/workflows/build/badge.svg)](https://github.com/zewish/prottle/actions?query=workflow%3Abuild) 3 | [![Downloads](https://img.shields.io/npm/dm/prottle.svg?style=flat-square)](https://www.npmjs.com/package/prottle) 4 | 5 | Promise.all() throttle - Prottle 6 | 7 | - Executes promise-returning functions in batches; 8 | - Once batch 1 is finished it's time for the next one; 9 | - Backend - Node 4.0+ supported; 10 | - Frontend - works with the env preset using babel. Use a Promise polyfill for IE. 11 | 12 | Installation 13 | ------------ 14 | ```bash 15 | $ npm install prottle --save 16 | ``` 17 | 18 | Example - resolved 19 | ------------------ 20 | ```js 21 | const prottle = require('prottle'); 22 | 23 | prottle(2, [ 24 | // batch 1 25 | () => Promise.resolve(1), 26 | () => Promise.resolve(2), 27 | // batch 2 28 | () => Promise.resolve(3), 29 | () => new Promise((resolve, reject) => { 30 | setTimeout(() => resolve(4), 3000); 31 | }), 32 | // batch 3 33 | () => Promise.resolve(5), 34 | ]) 35 | .then(res => { 36 | console.log(res); // [ 1, 2, 3, 4, 5 ] 37 | }); 38 | ``` 39 | 40 | Example - rejected 41 | ------------------ 42 | ```js 43 | const prottle = require('prottle'); 44 | 45 | prottle(2, [ 46 | () => Promise.resolve('yay'), 47 | () => Promise.reject('beep boop'), 48 | () => Promise.resolve('wow') 49 | ]) 50 | .catch(err => { 51 | console.log(err); // beep boop 52 | }); 53 | ``` 54 | 55 | Works with returned values too! 56 | ------------------------------- 57 | ```js 58 | const prottle = require('prottle'); 59 | 60 | prottle(2, [ 61 | () => 1, 62 | () => 2, 63 | () => 3 64 | ]) 65 | .then(res => { 66 | console.log(res); // [ 1, 2, 3 ] 67 | }); 68 | ``` 69 | -------------------------------------------------------------------------------- /prottle.test.js: -------------------------------------------------------------------------------- 1 | require('chai') 2 | .use(require('chai-as-promised')) 3 | .should(); 4 | 5 | describe('prottle.js', () => { 6 | let prottle; 7 | 8 | beforeEach(() => { 9 | prottle = require('./prottle.js'); 10 | }); 11 | 12 | it('rejects with limit error', () => { 13 | return prottle() 14 | .catch((err) => { 15 | err.message.should.equal('Limit must be at least 1'); 16 | }); 17 | }); 18 | 19 | it('rejects with limit error', () => { 20 | return prottle(0) 21 | .catch((err) => { 22 | err.message.should.equal('Limit must be at least 1'); 23 | }); 24 | }); 25 | 26 | it('rejects with limit error', () => { 27 | return prottle('dsad') 28 | .catch((err) => { 29 | err.message.should.equal('Limit must be at least 1'); 30 | }); 31 | }); 32 | 33 | it('rejects with array error', () => { 34 | return prottle(1, 'boo') 35 | .catch((err) => { 36 | err.message.should.equal('Array of promises required'); 37 | }); 38 | }); 39 | 40 | it('does not modify given array', () => { 41 | const arr = [ 42 | () => Promise.resolve(1), 43 | () => Promise.resolve(2), 44 | () => Promise.resolve(3) 45 | ]; 46 | 47 | let length = arr.length; 48 | 49 | return prottle(1, arr) 50 | .then(() => { 51 | arr.length.should.equal(length); 52 | }); 53 | }); 54 | 55 | it('resolves', () => { 56 | return prottle(3, [ 57 | () => Promise.resolve(1), 58 | () => Promise.resolve(2), 59 | () => Promise.resolve(3), 60 | () => Promise.resolve(4), 61 | () => Promise.resolve(5) 62 | ]) 63 | .should.eventually.eql([ 64 | 1, 2, 3, 4, 5 65 | ]); 66 | }); 67 | 68 | it('rejects', () => { 69 | return prottle(2, [ 70 | () => Promise.resolve(1), 71 | () => Promise.resolve(2), 72 | () => Promise.resolve(3), 73 | () => Promise.reject('beep boop'), 74 | () => Promise.resolve(5) 75 | ]) 76 | .should.be.rejectedWith('beep boop'); 77 | }); 78 | }); 79 | --------------------------------------------------------------------------------