All files / modules/10-common/utils PQueue.ts

66.67% Statements 24/36
31.25% Branches 5/16
77.78% Functions 7/9
66.67% Lines 24/36

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78                33x   33x 25x 25x 25x 25x     25x           1x 1x 1x 1x 1x 1x 1x   1x 1x 1x 1x                             1x 1x               7x     7x       1x       1x                
/*
 * Copyright 2021 Harness Inc. All rights reserved.
 * Use of this source code is governed by the PolyForm Shield 1.0.0 license
 * that can be found in the licenses directory at the root of this repository, also available at
 * https://polyformproject.org/wp-content/uploads/2020/06/PolyForm-Shield-1.0.0.txt.
 */
 
export type PromiseThunk = (signal: AbortSignal) => Promise<unknown>
import { v4 as uuid } from 'uuid'
 
export class PQueue {
  private queue: Array<() => void> = []
  private runningCount = 0
  private concurrency = Infinity
  private controllerMap = new Map<string, AbortController>()
 
  constructor(concurrency?: number) {
    Iif (typeof concurrency === 'number' && concurrency >= 1) {
      this.concurrency = concurrency
    }
  }
 
  add(fn: PromiseThunk): Promise<unknown> {
    return new Promise((resolve, reject) => {
      const run = (): void => {
        this.runningCount++
        const uid = uuid()
        const controller = new AbortController()
        this.controllerMap.set(uid, controller)
        fn(controller.signal).then(
          (value: unknown) => {
            resolve(value)
            this.controllerMap.delete(uid)
            this.runningCount--
            this.next()
          },
          (err: Error) => {
            this.controllerMap.delete(uid)
            if (err instanceof DOMException && err.name === 'AbortError') {
              // do nothing
            } else {
              reject(err)
            }
            this.runningCount--
            this.next()
          }
        )
      }
 
      Eif (this.runningCount < this.concurrency) {
        run()
      } else {
        this.queue.push(run)
      }
    })
  }
 
  cancel(): void {
    this.controllerMap.forEach(controller => {
      controller.abort()
    })
    this.controllerMap.clear()
  }
 
  private next(): void {
    Iif (this.runningCount >= this.concurrency) {
      return
    }
 
    Iif (this.queue.length > 0) {
      const task = this.queue.shift()
      if (task) {
        task()
      }
    }
  }
}