├── .npmignore ├── yarn.lock ├── index.js ├── example ├── package.json ├── yarn.lock ├── limitRequest.js ├── index.js └── request.js ├── package.json ├── .gitignore ├── lib └── LimitPromise.js └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | example/ 2 | .gitignore 3 | .idea 4 | *.iml 5 | *.log 6 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | const LimitPromise = require('./lib/LimitPromise') 3 | 4 | module.exports = LimitPromise 5 | module.exports.default = LimitPromise 6 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "description": "example for limit-promise", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "jialing lee", 10 | "license": "MIT", 11 | "dependencies": { 12 | "limit-promise": "^1.0.6" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /example/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | limit-promise@^1.0.6: 6 | version "1.0.6" 7 | resolved "https://registry.yarnpkg.com/limit-promise/-/limit-promise-1.0.6.tgz#88d768585c1bec975b686f9c1f90635076444ec0" 8 | integrity sha512-jvNHctXG7ClagHJvUR/ibjCGFI4+NT2mqoq1htKyvRkhNosOzeJia9j87qrzuzbXChSgu0IhEcP7OSdnbGe4Pg== 9 | -------------------------------------------------------------------------------- /example/limitRequest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jialing on 2019-10-02. 3 | */ 4 | 5 | const LimitPromise = require('limit-promise') 6 | const request = require('./request') 7 | 8 | const MAX = 5 9 | 10 | const limitP = new LimitPromise(MAX) 11 | 12 | function get (url, params) { 13 | return limitP.call(request.get, url, params) 14 | } 15 | 16 | function post (url, params) { 17 | return limitP.call(request.post, url, params) 18 | } 19 | 20 | module.exports = {get, post} 21 | 22 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jialing on 2019-10-02. 3 | */ 4 | 5 | const request = require('./limitRequest') 6 | 7 | const apis = [ 8 | {url: 'www.baidu.com'}, 9 | {url: 'www.taobao.com'}, 10 | {url: 'www.qq.com'}, 11 | {url: 'www.jd.com'}, 12 | {url: 'www.meituan.com'}, 13 | {url: 'www.toutiao.com'}, 14 | {url: 'www.weibo.com'}, 15 | {url: 'www.zhihu.com'}, 16 | {url: 'www.alipay.com'} 17 | ] 18 | 19 | for (let i = 0; i < apis.length; i++) { 20 | request.get(apis[i].url, {id: i}) 21 | .then(res => console.log(`request resolve : ${res}`)) 22 | } 23 | -------------------------------------------------------------------------------- /example/request.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jialing on 2019-10-02. 3 | * 4 | * 模拟网络请求request 5 | */ 6 | 7 | function get (url, params) { 8 | return mock('GET', url, params) 9 | } 10 | 11 | function post (url, params) { 12 | return mock('POST', url, params) 13 | } 14 | 15 | function mock (method, url, params) { 16 | // random timeout 17 | const time = Math.floor(Math.random() * 1000) 18 | return new Promise((resolve, reject) => { 19 | console.log(`request begin : ${method} ${url}, params = ${params && JSON.stringify(params)}`) 20 | setTimeout(() => { 21 | resolve(url) 22 | console.log(`request end : ${method} ${url}, params = ${params && JSON.stringify(params)}`) 23 | }, time) 24 | }) 25 | } 26 | 27 | module.exports = {get, post} 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "limit-promise", 3 | "version": "1.0.6", 4 | "description": "run promise or async-function with limited concurrency", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/leejialing/limit-promise.git" 12 | }, 13 | "keywords": [ 14 | "javascript", 15 | "es6", 16 | "promise", 17 | "limit", 18 | "limit promise" 19 | ], 20 | "author": "jialing Lee", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/leejialing/limit-promise/issues" 24 | }, 25 | "homepage": "https://github.com/leejialing/limit-promise#readme", 26 | "devDependencies": {}, 27 | "dependencies": { 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # editor 64 | .idea 65 | .vscode 66 | -------------------------------------------------------------------------------- /lib/LimitPromise.js: -------------------------------------------------------------------------------- 1 | const LimitPromise = function (max) { 2 | this._max = max 3 | this._count = 0 4 | this._taskQueue = [] 5 | } 6 | 7 | LimitPromise.prototype.call = function (caller, ...args) { 8 | return new Promise((resolve, reject) => { 9 | const task = this._createTask(caller, args, resolve, reject) 10 | if (this._count >= this._max) { 11 | // console.log('count >= max, push a task to queue') 12 | this._taskQueue.push(task) 13 | } else { 14 | task() 15 | } 16 | }) 17 | } 18 | 19 | LimitPromise.prototype._createTask = function (caller, args, resolve, reject) { 20 | return () => { 21 | caller(...args) 22 | .then(resolve) 23 | .catch(reject) 24 | .finally(() => { 25 | this._count-- 26 | if (this._taskQueue.length) { 27 | // console.log('a task run over, pop a task to run') 28 | let task = this._taskQueue.shift() 29 | task() 30 | } else { 31 | // console.log('task count = ', count) 32 | } 33 | }) 34 | this._count++ 35 | // console.log('task run , task count = ', count) 36 | } 37 | } 38 | 39 | if (typeof Promise.prototype.finally !== 'function') { 40 | Promise.prototype.finally = function (callback) { 41 | let P = this.constructor 42 | return this.then( 43 | value => P.resolve(callback()).then(() => value), 44 | reason => P.resolve(callback()).then(() => { 45 | throw reason 46 | }) 47 | ) 48 | } 49 | } 50 | 51 | module.exports = LimitPromise 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # limit promise 2 | EN: 3 | Run multiple promise-returning & async functions with limited concurrency 4 | 5 | CN: 6 | Promise执行数量控制,即"并发"控制(众所周知js是单线程,并不存在真正的并发,只是形象说明才这么称呼的)。 7 | 8 | 应用场景如:控制异步请求的数量。 9 | 10 | ## Install (安装) 11 | 12 | `npm install limit-promise --save` 13 | or 14 | `yarn add limit-promise` 15 | 16 | ## Tip 17 | EN: 18 | Make sure that the function you want to execute is: 19 | * returns a Promise that matches the Promise criteria 20 | * async function 21 | * make sure Promise resolves or reject, otherwise it will "block" the call queue 22 | 23 | If it doesn't meet the standard, please adapt yourself 24 | 25 | CN: 26 | 确保需要执行的函数是: 27 | * 返回一个符合Promise标准的promise 28 | * async函数 29 | * 确保Promise会resolve或者reject,否则会"阻塞"住调用队列。 30 | 31 | 如果不符合标准请自行适配一下。 32 | 33 | ## Usage(使用) 34 | 35 | ```js 36 | 37 | // request.js 38 | // a request module, contain 'get' and 'post' function 39 | 40 | // limitRequest.js 41 | const LimitPromise = require('limit-promise') 42 | const request = require('./request') 43 | // request maximum limit 并发请求上限 44 | const MAX = 5 45 | // core controller 核心控制器 46 | const limitP = new LimitPromise(MAX) 47 | 48 | // 通过控制器包装get和post方法,实际上是将请求函数递交给控制器处理 49 | // Wrapping the get and post methods with the controller, 50 | // The request function is actually handed over to the controller for processing 51 | function get (url, params) { 52 | return limitP.call(request.get, url, params) 53 | } 54 | 55 | function post (url, params) { 56 | return limitP.call(request.post, url, params) 57 | } 58 | 59 | module.exports = {get, post} 60 | 61 | // example.js 62 | 63 | // import new request module 64 | const request = require('./limitRequest') 65 | // mock apis 66 | const apis = [ 67 | {url: 'www.baidu.com'}, 68 | {url: 'www.taobao.com'}, 69 | {url: 'www.qq.com'}, 70 | {url: 'www.jd.com'}, 71 | {url: 'www.meituan.com'}, 72 | {url: 'www.toutiao.com'}, 73 | {url: 'www.weibo.com'}, 74 | {url: 'www.zhihu.com'}, 75 | {url: 'www.alipay.com'} 76 | ] 77 | 78 | // start multiple network request 79 | // Because the maximum number of requests is limited to 5, the first five requests will start immediately, and the sixth will start in the ready queue for execution 80 | // Once a request is completed and returned, a task is removed from the queue for execution 81 | // 简单粗暴的使用for循环启动数个请求 82 | // 由于限制了最大请求数量是5,因此前五个请求将立即启动,而第6个开始将进入就绪队列等待执行 83 | // 一旦某个请求结束并返回,将从队列中取下一个任务执行 84 | for (let i = 0; i < apis.length; i++) { 85 | request.get(apis[i].url, {id: i}) 86 | .then(res => console.log(`request resolve : ${res}`)) 87 | } 88 | 89 | ``` 90 | 91 | ## License 92 | *MIT* 93 | --------------------------------------------------------------------------------