| 
									
										
										
										
											2021-01-15 17:50:02 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @template T | 
					
						
							| 
									
										
										
										
											2021-04-12 15:17:43 +08:00
										 |  |  |  * @template {Error} E | 
					
						
							| 
									
										
										
										
											2021-01-15 17:50:02 +08:00
										 |  |  |  * @param {Iterable<T>} items initial items | 
					
						
							|  |  |  |  * @param {number} concurrency number of items running in parallel | 
					
						
							| 
									
										
										
										
											2021-04-12 15:17:43 +08:00
										 |  |  |  * @param {function(T, function(T): void, function(E=): void): void} processor worker which pushes more items | 
					
						
							|  |  |  |  * @param {function(E=): void} callback all items processed | 
					
						
							| 
									
										
										
										
											2021-01-15 17:50:02 +08:00
										 |  |  |  * @returns {void} | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const processAsyncTree = (items, concurrency, processor, callback) => { | 
					
						
							|  |  |  | 	const queue = Array.from(items); | 
					
						
							|  |  |  | 	if (queue.length === 0) return callback(); | 
					
						
							|  |  |  | 	let processing = 0; | 
					
						
							|  |  |  | 	let finished = false; | 
					
						
							|  |  |  | 	let processScheduled = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const push = item => { | 
					
						
							|  |  |  | 		queue.push(item); | 
					
						
							|  |  |  | 		if (!processScheduled && processing < concurrency) { | 
					
						
							|  |  |  | 			processScheduled = true; | 
					
						
							|  |  |  | 			process.nextTick(processQueue); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const processorCallback = err => { | 
					
						
							|  |  |  | 		processing--; | 
					
						
							|  |  |  | 		if (err && !finished) { | 
					
						
							|  |  |  | 			finished = true; | 
					
						
							|  |  |  | 			callback(err); | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (!processScheduled) { | 
					
						
							|  |  |  | 			processScheduled = true; | 
					
						
							|  |  |  | 			process.nextTick(processQueue); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const processQueue = () => { | 
					
						
							|  |  |  | 		if (finished) return; | 
					
						
							|  |  |  | 		while (processing < concurrency && queue.length > 0) { | 
					
						
							|  |  |  | 			processing++; | 
					
						
							|  |  |  | 			const item = queue.pop(); | 
					
						
							|  |  |  | 			processor(item, push, processorCallback); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		processScheduled = false; | 
					
						
							|  |  |  | 		if (queue.length === 0 && processing === 0 && !finished) { | 
					
						
							|  |  |  | 			finished = true; | 
					
						
							|  |  |  | 			callback(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	processQueue(); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = processAsyncTree; |