|  |  |  | /* global clearTimeout, log */ | 
					
						
							|  |  |  | // was timeoutDelay
 | 
					
						
							|  |  |  | const sleepFor = ms => new Promise(resolve => setTimeout(resolve, ms)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Taken from https://stackoverflow.com/questions/51160260/clean-way-to-wait-for-first-true-returned-by-promise
 | 
					
						
							|  |  |  | // The promise returned by this function will resolve true when the first promise
 | 
					
						
							|  |  |  | // in ps resolves true *or* it will resolve false when all of ps resolve false
 | 
					
						
							|  |  |  | const firstTrue = ps => { | 
					
						
							|  |  |  |   const newPs = ps.map( | 
					
						
							|  |  |  |     p => | 
					
						
							|  |  |  |       new Promise( | 
					
						
							|  |  |  |         // eslint-disable-next-line more/no-then
 | 
					
						
							|  |  |  |         (resolve, reject) => p.then(v => v && resolve(v), reject) | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   // eslint-disable-next-line more/no-then
 | 
					
						
							|  |  |  |   newPs.push(Promise.all(ps).then(() => false)); | 
					
						
							|  |  |  |   return Promise.race(newPs); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // one action resolves all
 | 
					
						
							|  |  |  | const snodeGlobalLocks = {}; | 
					
						
							|  |  |  | async function allowOnlyOneAtATime(name, process, timeout) { | 
					
						
							|  |  |  |   // if currently not in progress
 | 
					
						
							|  |  |  |   if (snodeGlobalLocks[name] === undefined) { | 
					
						
							|  |  |  |     // set lock
 | 
					
						
							|  |  |  |     snodeGlobalLocks[name] = new Promise(async (resolve, reject) => { | 
					
						
							|  |  |  |       // set up timeout feature
 | 
					
						
							|  |  |  |       let timeoutTimer = null; | 
					
						
							|  |  |  |       if (timeout) { | 
					
						
							|  |  |  |         timeoutTimer = setTimeout(() => { | 
					
						
							|  |  |  |           log.warn( | 
					
						
							|  |  |  |             `loki_primitives:::allowOnlyOneAtATime - TIMEDOUT after ${timeout}s` | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |           delete snodeGlobalLocks[name]; // clear lock
 | 
					
						
							|  |  |  |           reject(); | 
					
						
							|  |  |  |         }, timeout); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       // do actual work
 | 
					
						
							|  |  |  |       let innerRetVal; | 
					
						
							|  |  |  |       try { | 
					
						
							|  |  |  |         innerRetVal = await process(); | 
					
						
							|  |  |  |       } catch (e) { | 
					
						
							|  |  |  |         log.error( | 
					
						
							|  |  |  |           `loki_primitives:::allowOnlyOneAtATime - error ${e.code} ${e.message}` | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |         // clear timeout timer
 | 
					
						
							|  |  |  |         if (timeout) { | 
					
						
							|  |  |  |           if (timeoutTimer !== null) { | 
					
						
							|  |  |  |             clearTimeout(timeoutTimer); | 
					
						
							|  |  |  |             timeoutTimer = null; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         delete snodeGlobalLocks[name]; // clear lock
 | 
					
						
							|  |  |  |         throw e; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       // clear timeout timer
 | 
					
						
							|  |  |  |       if (timeout) { | 
					
						
							|  |  |  |         if (timeoutTimer !== null) { | 
					
						
							|  |  |  |           clearTimeout(timeoutTimer); | 
					
						
							|  |  |  |           timeoutTimer = null; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       delete snodeGlobalLocks[name]; // clear lock
 | 
					
						
							|  |  |  |       // release the kraken
 | 
					
						
							|  |  |  |       resolve(innerRetVal); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return snodeGlobalLocks[name]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function abortableIterator(array, iterator) { | 
					
						
							|  |  |  |   let abortIteration = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // for the control promise
 | 
					
						
							|  |  |  |   let controlResolveFunctor; | 
					
						
							|  |  |  |   const stopPolling = new Promise(res => { | 
					
						
							|  |  |  |     // store resolve functor
 | 
					
						
							|  |  |  |     controlResolveFunctor = res; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // eslint-disable-next-line more/no-then
 | 
					
						
							|  |  |  |   stopPolling.then(() => { | 
					
						
							|  |  |  |     abortIteration = true; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const destructableList = [...array]; | 
					
						
							|  |  |  |   const accum = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     start: async serially => { | 
					
						
							|  |  |  |       let item = destructableList.pop(); | 
					
						
							|  |  |  |       while (item && !abortIteration) { | 
					
						
							|  |  |  |         if (serially) { | 
					
						
							|  |  |  |           // eslint-disable-next-line no-await-in-loop
 | 
					
						
							|  |  |  |           accum.push(await iterator(item)); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           accum.push(iterator(item)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         item = destructableList.pop(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return accum; | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     stop: () => { | 
					
						
							|  |  |  |       controlResolveFunctor(); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = { | 
					
						
							|  |  |  |   sleepFor, | 
					
						
							|  |  |  |   allowOnlyOneAtATime, | 
					
						
							|  |  |  |   abortableIterator, | 
					
						
							|  |  |  |   firstTrue, | 
					
						
							|  |  |  | }; |