queue

Asynchronous function queue with adjustable concurrency.

Этот скрипт недоступен для установки пользователем. Он является библиотекой, которая подключается к другим скриптам мета-ключом // @require https://update.greasyfork.org/scripts/482311/1297431/queue.js

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// @name               queue
// @description        Asynchronous function queue with adjustable concurrency.
// @author             Jesse Tane
// @namespace          https://github.com/jessetane/queue
// @version            7.0.0
// @license            MIT
// ==/UserScript==

var { Queue, QueueEvent } = (function (exports) {
  'use strict';

  const has = Object.prototype.hasOwnProperty;

  /**
   * Since CustomEvent is only supported in nodejs since version 19,
   * you have to create your own class instead of using CustomEvent
   * @see https://github.com/nodejs/node/issues/40678
   * */
  class QueueEvent extends Event {
    constructor (name, detail) {
      super(name);
      this.detail = detail;
    }
  }

  class Queue extends EventTarget {
    constructor (options = {}) {
      super();
      const { concurrency = Infinity, timeout = 0, autostart = false, results = null } = options;
      this.concurrency = concurrency;
      this.timeout = timeout;
      this.autostart = autostart;
      this.results = results;
      this.pending = 0;
      this.session = 0;
      this.running = false;
      this.jobs = [];
      this.timers = [];
      this.addEventListener('error', this._errorHandler);
    }

    _errorHandler (evt) {
      this.end(evt.detail.error);
    }

    pop () {
      return this.jobs.pop()
    }

    shift () {
      return this.jobs.shift()
    }

    indexOf (searchElement, fromIndex) {
      return this.jobs.indexOf(searchElement, fromIndex)
    }

    lastIndexOf (searchElement, fromIndex) {
      if (fromIndex !== undefined) return this.jobs.lastIndexOf(searchElement, fromIndex)
      return this.jobs.lastIndexOf(searchElement)
    }

    slice (start, end) {
      this.jobs = this.jobs.slice(start, end);
      return this
    }

    reverse () {
      this.jobs.reverse();
      return this
    }

    push (...workers) {
      const methodResult = this.jobs.push(...workers);
      if (this.autostart) this._start();
      return methodResult
    }

    unshift (...workers) {
      const methodResult = this.jobs.unshift(...workers);
      if (this.autostart) this._start();
      return methodResult
    }

    splice (start, deleteCount, ...workers) {
      this.jobs.splice(start, deleteCount, ...workers);
      if (this.autostart) this._start();
      return this
    }

    get length () {
      return this.pending + this.jobs.length
    }

    start (callback) {
      if (this.running) throw new Error('already started')
      let awaiter;
      if (callback) {
        this._addCallbackToEndEvent(callback);
      } else {
        awaiter = this._createPromiseToEndEvent();
      }
      this._start();
      return awaiter
    }

    _start () {
      this.running = true;
      if (this.pending >= this.concurrency) {
        return
      }
      if (this.jobs.length === 0) {
        if (this.pending === 0) {
          this.done();
        }
        return
      }
      const job = this.jobs.shift();
      const session = this.session;
      const timeout = (job !== undefined) && has.call(job, 'timeout') ? job.timeout : this.timeout;
      let once = true;
      let timeoutId = null;
      let didTimeout = false;
      let resultIndex = null;
      const next = (error, ...result) => {
        if (once && this.session === session) {
          once = false;
          this.pending--;
          if (timeoutId !== null) {
            this.timers = this.timers.filter(tID => tID !== timeoutId);
            clearTimeout(timeoutId);
          }
          if (error) {
            this.dispatchEvent(new QueueEvent('error', { error, job }));
          } else if (!didTimeout) {
            if (resultIndex !== null && this.results !== null) {
              this.results[resultIndex] = [...result];
            }
            this.dispatchEvent(new QueueEvent('success', { result: [...result], job }));
          }
          if (this.session === session) {
            if (this.pending === 0 && this.jobs.length === 0) {
              this.done();
            } else if (this.running) {
              this._start();
            }
          }
        }
      };
      if (timeout) {
        timeoutId = setTimeout(() => {
          didTimeout = true;
          this.dispatchEvent(new QueueEvent('timeout', { next, job }));
          next();
        }, timeout);
        this.timers.push(timeoutId);
      }
      if (this.results != null) {
        resultIndex = this.results.length;
        this.results[resultIndex] = null;
      }
      this.pending++;
      this.dispatchEvent(new QueueEvent('start', { job }));
      job.promise = job(next);
      if (job.promise !== undefined && typeof job.promise.then === 'function') {
        job.promise.then(function (result) {
          return next(undefined, result)
        }).catch(function (err) {
          return next(err || true)
        });
      }
      if (this.running && this.jobs.length > 0) {
        this._start();
      }
    }

    stop () {
      this.running = false;
    }

    end (error) {
      this.clearTimers();
      this.jobs.length = 0;
      this.pending = 0;
      this.done(error);
    }

    clearTimers () {
      this.timers.forEach(timer => {
        clearTimeout(timer);
      });
      this.timers = [];
    }

    _addCallbackToEndEvent (cb) {
      const onend = evt => {
        this.removeEventListener('end', onend);
        cb(evt.detail.error, this.results);
      };
      this.addEventListener('end', onend);
    }

    _createPromiseToEndEvent () {
      return new Promise((resolve, reject) => {
        this._addCallbackToEndEvent((error, results) => {
          if (error) reject(error);
          else resolve(results);
        });
      })
    }

    done (error) {
      this.session++;
      this.running = false;
      this.dispatchEvent(new QueueEvent('end', { error }));
    }
  }

  exports.Queue = Queue;
  exports.QueueEvent = QueueEvent;

  return exports;

})({});