Compare commits
	
		
			6 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 178672ad0d | |||
| 9beae76631 | |||
| dd206891d5 | |||
| 038b6cac33 | |||
| b193312ed6 | |||
| 81333f4c47 | 
							
								
								
									
										18
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								README.md
									
									
									
									
									
								
							| @ -3,13 +3,17 @@ | |||||||
| Like calling `map()` and resolving with `Promise.all()`, | Like calling `map()` and resolving with `Promise.all()`, | ||||||
| but handling a bounded number of items at any given time. | but handling a bounded number of items at any given time. | ||||||
| 
 | 
 | ||||||
| ## Install | Want to read about it? | ||||||
|  | 
 | ||||||
|  | - [Batching async requests in < 50 lines of VanillaJS](https://coolaj86.com/articles/batching-async-requests-50-lines-of-vanilla-js/) | ||||||
|  | 
 | ||||||
|  | # Install | ||||||
| 
 | 
 | ||||||
| ```bash | ```bash | ||||||
| npm install --save batchasync | npm install --save batchasync | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## Usage | # Usage | ||||||
| 
 | 
 | ||||||
| ```js | ```js | ||||||
| // Browsers | // Browsers | ||||||
| @ -35,10 +39,14 @@ batchAsync(batchSize, things, doStuff).then(function(results) { | |||||||
| }); | }); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ### Returning 'undefined' | ## A note on `undefined` | ||||||
| 
 | 
 | ||||||
| Returning `undefined` will cause an exception to be thrown | Returning `undefined` will cause an exception to be thrown, | ||||||
| (because it looks like a skipped promise). Return `null` instead. | and the Promise to be rejected. | ||||||
|  | 
 | ||||||
|  | This is on purpose - because `undefined` is indistinguishable from a skipped Promise. | ||||||
|  | 
 | ||||||
|  | Return `null` instead. | ||||||
| 
 | 
 | ||||||
| **Example (bad)**: | **Example (bad)**: | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										127
									
								
								batchasync.js
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								batchasync.js
									
									
									
									
									
								
							| @ -1,71 +1,70 @@ | |||||||
| (function (exports) { | (function(exports) { | ||||||
| 'use strict'; |     "use strict"; | ||||||
| 
 | 
 | ||||||
| exports.batchAsync = function (limit, arr, doStuff) { |     exports.batchAsync = function(limit, arr, doStuff) { | ||||||
| 	arr = arr.slice(0); |         arr = arr.slice(0); | ||||||
| 	return new Promise(function(resolve, reject) { |         return new Promise(function(resolve, reject) { | ||||||
| 		var total = arr.length; |             var total = arr.length; | ||||||
| 		var active = 0; |             var active = 0; | ||||||
| 		var results = []; |             var results = []; | ||||||
| 		var error; |             var error; | ||||||
| 
 | 
 | ||||||
| 		function doMoreStuff() { |             function doMoreStuff() { | ||||||
| 			// Don't take on any more tasks if we've errored,
 |                 // Don't take on any more tasks if we've errored,
 | ||||||
| 			// or if too many are already in progress
 |                 // or if too many are already in progress
 | ||||||
| 			if (error || active > limit) { |                 if (error || active > limit) { | ||||||
| 				return; |                     return; | ||||||
| 			} |                 } | ||||||
| 
 | 
 | ||||||
| 			// If there are no more tasks to start, return
 |                 // If there are no more tasks to start, return
 | ||||||
| 			if (!arr.length) { |                 if (!arr.length) { | ||||||
| 				// If everything is also *finished*, resolve
 |                     // If everything is also *finished*, resolve
 | ||||||
| 				if (active < 1) { |                     if (active < 1) { | ||||||
| 					resolve(results); |                         resolve(results); | ||||||
| 				} |                     } | ||||||
| 				return; |                     return; | ||||||
| 			} |                 } | ||||||
| 
 | 
 | ||||||
| 			// We need to dequeue the task here so the index is correct
 |                 // We need to dequeue the task here so the index is correct
 | ||||||
| 			// (keep in mind we want to support sync and async)
 |                 // (keep in mind we want to support sync and async)
 | ||||||
| 			var index = total - arr.length; |                 var index = total - arr.length; | ||||||
| 			var task = arr.shift(); |                 var task = arr.shift(); | ||||||
| 			active += 1; |                 active += 1; | ||||||
| 
 | 
 | ||||||
| 			// Spawn another task immediately,
 |                 // Spawn another task immediately,
 | ||||||
| 			// which will be stopped if we're at the limit
 |                 // which will be stopped if we're at the limit
 | ||||||
| 			doMoreStuff(); |                 doMoreStuff(); | ||||||
| 
 | 
 | ||||||
| 			var p; |                 var p; | ||||||
| 			try { |                 try { | ||||||
| 				p = doStuff(task); |                     p = doStuff(task, index, arr); | ||||||
| 			} catch (e) { |                 } catch (e) { | ||||||
| 				// we need to handle, and bubble, synchronous errors
 |                     // we need to handle, and bubble, synchronous errors
 | ||||||
| 				error = e; |                     error = e; | ||||||
| 				reject(e); |                     reject(e); | ||||||
| 				throw e; |                     throw e; | ||||||
| 			} |                 } | ||||||
| 			// Do stuff and then decrease the active counter when done
 |                 // Do stuff and then decrease the active counter when done
 | ||||||
| 			// add support for sync by rapping in a promise
 |                 // add support for sync by rapping in a promise
 | ||||||
| 			Promise.resolve(p) |                 Promise.resolve(p) | ||||||
| 				.then(function(result) { |                     .then(function(result) { | ||||||
| 					if ('undefined' === typeof result) { |                         if ("undefined" === typeof result) { | ||||||
| 						throw new Error( |                             throw new Error( | ||||||
| 							"result was 'undefined'. Please return 'null' to signal that you didn't just forget to return another promise." |                                 "result was 'undefined'. Please return 'null' to signal that you didn't just forget to return another promise." | ||||||
| 						); |                             ); | ||||||
| 					} |                         } | ||||||
| 					active -= 1; |                         active -= 1; | ||||||
| 					results[index] = result; |                         results[index] = result; | ||||||
| 				}) |                     }) | ||||||
| 				.then(doMoreStuff) |                     .then(doMoreStuff) | ||||||
| 				.catch(function(e) { |                     .catch(function(e) { | ||||||
| 					// handle async errors
 |                         // handle async errors
 | ||||||
| 					error = e; |                         error = e; | ||||||
| 					reject(e); |                         reject(e); | ||||||
| 				}); |                     }); | ||||||
| 		} |             } | ||||||
| 
 | 
 | ||||||
| 		doMoreStuff(); |             doMoreStuff(); | ||||||
| 	}); |         }); | ||||||
| }; |     }; | ||||||
| 
 | })("undefined" !== typeof window ? window : module.exports); | ||||||
| }('undefined' !== typeof window ? window : module.exports)); |  | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | { | ||||||
|  |   "name": "batchasync", | ||||||
|  |   "version": "1.0.3", | ||||||
|  |   "lockfileVersion": 1 | ||||||
|  | } | ||||||
| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "batchasync", |   "name": "batchasync", | ||||||
|   "version": "1.0.1", |   "version": "1.0.3", | ||||||
|   "description": "Like forEachAsync, or Promise.all(), but handling a bounded number of items at any given time.", |   "description": "Like forEachAsync, or Promise.all(), but handling a bounded number of items at any given time.", | ||||||
|   "main": "batchasync.js", |   "main": "batchasync.js", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|  | |||||||
							
								
								
									
										211
									
								
								test.js
									
									
									
									
									
								
							
							
						
						
									
										211
									
								
								test.js
									
									
									
									
									
								
							| @ -1,110 +1,111 @@ | |||||||
| (function (exports) { | (function(exports) { | ||||||
| 'use strict'; |     "use strict"; | ||||||
| 
 | 
 | ||||||
| var batchAsync = exports.batchAsync || require('./batchasync.js').batchAsync; |     var batchAsync = | ||||||
|  |         exports.batchAsync || require("./batchasync.js").batchAsync; | ||||||
| 
 | 
 | ||||||
| function testBatch() { |     function testBatch() { | ||||||
| 	var timeouts = [100, 80, 20, 500, 50, 30, 200, 300]; |         var timeouts = [100, 80, 20, 500, 50, 30, 200, 300]; | ||||||
| 	var tasks = timeouts.map(function(timeout, i) { |         console.info(timeouts); | ||||||
| 		return function() { |         var tasks = timeouts.map(function(timeout, i) { | ||||||
| 			return promiseTimeout(timeout).then(function() { |             return function() { | ||||||
| 				console.log('task:', i, timeouts[i]); |                 return promiseTimeout(timeout).then(function() { | ||||||
| 				return i; |                     //console.log("task:", i, timeouts[i]);
 | ||||||
| 			}); |                     return i + ":" + timeouts[i]; | ||||||
| 		}; |                 }); | ||||||
| 	}); |             }; | ||||||
| 	var len = tasks.length; |         }); | ||||||
|  |         var len = tasks.length; | ||||||
| 
 | 
 | ||||||
| 	return batchAsync(4, tasks, function(task) { |         return batchAsync(4, tasks, function(task) { | ||||||
| 		return task(); |             return task(); | ||||||
| 	}) |         }) | ||||||
| 		.then(function(results) { |             .then(function(results) { | ||||||
| 			console.info('results:', results); |                 console.info("results:", results); | ||||||
| 			if (len !== results.length) { |                 if (len !== results.length) { | ||||||
| 				throw new Error('result set too small'); |                     throw new Error("result set too small"); | ||||||
| 			} |                 } | ||||||
| 			if (results.join(' ') !== results.sort().join(' ')) { |                 if (results.join(" ") !== results.sort().join(" ")) { | ||||||
| 				throw new Error('result set out-of-order'); |                     throw new Error("result set out-of-order"); | ||||||
| 			} |                 } | ||||||
| 		}) |             }) | ||||||
| 		.then(function() { |             .then(function() { | ||||||
| 			return batchAsync(4, [], 'not a function').then(function() { |                 return batchAsync(4, [], "not a function").then(function() { | ||||||
| 				console.info('Handled ZERO tasks correctly.'); |                     console.info("Handled ZERO tasks correctly."); | ||||||
| 			}); |                 }); | ||||||
| 		}) |             }) | ||||||
| 		.then(function() { |             .then(function() { | ||||||
| 			return batchAsync(4, timeouts, function(x) { |                 return batchAsync(4, timeouts, function(x) { | ||||||
| 				return x; |                     return x; | ||||||
| 			}).then(function(results) { |                 }).then(function(results) { | ||||||
| 				if (results.join(' ') !== timeouts.join(' ')) { |                     if (results.join(" ") !== timeouts.join(" ")) { | ||||||
| 					console.error(results); |                         console.error(results); | ||||||
| 					throw new Error('sync result set out-of-order'); |                         throw new Error("sync result set out-of-order"); | ||||||
| 				} |                     } | ||||||
| 				console.info('Handled sync tasks correctly.'); |                     console.info("Handled sync tasks correctly."); | ||||||
| 			}); |                 }); | ||||||
| 		}) |             }) | ||||||
| 		.then(function() { |             .then(function() { | ||||||
| 			return batchAsync(4, tasks, function(task) { |                 return batchAsync(4, tasks, function(task) { | ||||||
| 				if (0 === Math.floor(Math.random() * 2) % 2) { |                     if (0 === Math.floor(Math.random() * 2) % 2) { | ||||||
| 					throw new Error('any async error will do'); |                         throw new Error("any async error will do"); | ||||||
| 				} |                     } | ||||||
| 				return task(); |                     return task(); | ||||||
| 			}) |                 }) | ||||||
| 				.then(function(results) { |                     .then(function(results) { | ||||||
| 					console.log(results); |                         console.log(results); | ||||||
| 					var e = new Error('async rejection should not pass!'); |                         var e = new Error("async rejection should not pass!"); | ||||||
| 					e.FAIL = true; |                         e.FAIL = true; | ||||||
| 					throw e; |                         throw e; | ||||||
| 				}) |                     }) | ||||||
| 				.catch(function(e) { |                     .catch(function(e) { | ||||||
| 					if (e.FAIL) { |                         if (e.FAIL) { | ||||||
| 						throw e; |                             throw e; | ||||||
| 					} |                         } | ||||||
| 					console.info('Pass: Exception thrown when expected'); |                         console.info("Pass: Exception thrown when expected"); | ||||||
| 				}); |                     }); | ||||||
| 		}) |             }) | ||||||
| 		.then(function() { |             .then(function() { | ||||||
| 			return batchAsync(4, timeouts, function() { |                 return batchAsync(4, timeouts, function() { | ||||||
| 				if (0 === Math.floor(Math.random() * 2) % 2) { |                     if (0 === Math.floor(Math.random() * 2) % 2) { | ||||||
| 					throw new Error('any sync error will do'); |                         throw new Error("any sync error will do"); | ||||||
| 				} |                     } | ||||||
|         return null; |                     return null; | ||||||
| 			}) |                 }) | ||||||
| 				.then(function(results) { |                     .then(function(/*results*/) { | ||||||
| 					var e = new Error('should not pass sync exception!'); |                         var e = new Error("should not pass sync exception!"); | ||||||
| 					e.FAIL = true; |                         e.FAIL = true; | ||||||
| 					throw e; |                         throw e; | ||||||
| 				}) |                     }) | ||||||
| 				.catch(function(e) { |                     .catch(function(e) { | ||||||
| 					if (e.FAIL) { |                         if (e.FAIL) { | ||||||
| 						throw e; |                             throw e; | ||||||
| 					} |                         } | ||||||
| 				}) |                     }) | ||||||
| 				.then(function() { |                     .then(function() { | ||||||
| 					// wait for the tasks the error left dangling to print their message
 |                         // wait for the tasks the error left dangling to print their message
 | ||||||
| 					console.info('Pass: Promise rejected when expected'); |                         console.info("Pass: Promise rejected when expected"); | ||||||
| 					return promiseTimeout(1000); |                         return promiseTimeout(1000); | ||||||
| 				}); |                     }); | ||||||
| 		}); |             }); | ||||||
| } |     } | ||||||
| 
 | 
 | ||||||
| function promiseTimeout(timeout) { |     function promiseTimeout(timeout) { | ||||||
| 	return new Promise(function(resolve, reject) { |         return new Promise(function(resolve) { | ||||||
| 		setTimeout(resolve, timeout); |             setTimeout(resolve, timeout); | ||||||
| 	}); |         }); | ||||||
| } |     } | ||||||
| 
 | 
 | ||||||
| testBatch() |     testBatch() | ||||||
| 	.then(function() { |         .then(function() { | ||||||
| 		console.info('PROBABLY PASSED'); |             console.info("PROBABLY PASSED"); | ||||||
| 		console.info( |             console.info( | ||||||
| 			'We tested what could be tested without knowing Passed what could be tested Do the results make sense?' |                 "We tested what could be tested. Do the results make sense?" | ||||||
| 		); |             ); | ||||||
| 	}) |         }) | ||||||
| 	.catch(function(e) { |         .catch(function(e) { | ||||||
| 		console.error('FAIL!'); |             console.error("FAIL!"); | ||||||
| 		console.error(e); |             console.error(e); | ||||||
|     process.exit(500); |             process.exit(500); | ||||||
| 	}); |         }); | ||||||
| 
 | })("undefined" !== typeof window ? window : module.exports); | ||||||
| }('undefined' !== typeof window ? window : module.exports)); |  | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user