function tryProcessNext() {
    while (tasks.length > 0 && runningCount < concurrency) {
      const item = tasks.shift();
      runningCount++;

      for (const cb of processingStartedCallbacks) {
        cb({ item });
      }

      const promise = Promise.resolve(processor(item));

      promise
        .then(() => {
          // Fire processingEnded callbacks
          for (const cb of processingEndedCallbacks) {
            cb({ item, err: undefined });
          }
        })
        .catch(error => {
          // Fire processingEnded callbacks with an error
          for (const cb of processingEndedCallbacks) {
            cb({ item, err: error });
          }
        })
        .finally(() => {
          runningCount--;
          checkDrained();
          tryProcessNext();
        });
    }
  }
Attempt to start processing more tasks if we have
capacity (runningCount < concurrency).