Сравнение мьютексных реализаций для асинхронного JavaScript - PullRequest
0 голосов
/ 24 октября 2019

Я нашел где-то реализацию шаблона мьютекса в JavaScript, который хорошо работает, но выглядит действительно сложным. Даже если я понимаю шаблон взаимного исключения, я не знаю, почему обещания в некоторых местах используются определенным образом. Но об этом через минуту. Я поиграл с этим и создал свою собственную реализацию, которая намного проще для понимания и содержит меньше кода. Вот проект: https://codesandbox.io/s/mutex-2e014

У вас есть 3 реализации шаблона мьютекса. Mutex 0 и 1 - это та же версия с этим отличием, что в версии 1 я упростил код, воспользовавшись функциями стрелок и получив доступ к ним из родительского контекста. Так что в целом это одни и те же решения. Мое решение - Mutex 2.

Вы можете протестировать код, открыв консоль и нажав кнопки от 0 до 2. Вы можете видеть, что каждое асинхронное действие занимает 1 секунду, и оно всегда будет выполнять операции в правильном порядке и ждатьпредыдущее задание закончить. Он работает одинаково во всех версиях. Также, когда вы удерживаете клавишу SHIFT, при нажатии кнопок выдается исключение, и эта часть также работает одинаково во всех версиях. Следующая вещь, которую я должен был сравнить, это возвращаемое значение, и это не отличается. Итак, мои вопросы: почему это так сложно в первых двух решениях? Я что-то пропустил? Это действительно работает так же?

Теперь вопрос об использовании обещаний, который я не понимаю. В файле Mutex1.ts в строке 7 мы имеем return new Promise(resolve);. Мы передаем разрешение из родительского обещания в качестве первого аргумента конструктора Promise, который является исполнителем. И из того, что я прочитал, исполнителей вызывают непосредственно перед созданием обещания. В этом случае, благодаря этому у нас есть доступ к функции unlock в методе run, но я не знаю, как это возможно, что this.lock возвращает функцию. Возможно, я не знаю обещаний достаточно хорошо. Есть идеи?

Mutex 0

export default class MutexA {
  private mutex = Promise.resolve();

  lock() {
    let begin = unlock => {};

    this.mutex = this.mutex.then(() => {
      return new Promise(begin);
    });

    return new Promise(res => {
      begin = res;
    });
  }

  async run(fn) {
    const unlock = await this.lock();
    try {
      return await Promise.resolve(fn());
    } finally {
      unlock();
    }
  }
}

Mutex 1

export default class MutexB {
  private mutex = Promise.resolve();

  lock() {
    return new Promise(resolve => {
      this.mutex = this.mutex.then(() => {
        return new Promise(resolve);
      });
    });
  }

  async run(fn) {
    const unlock = await this.lock();
    try {
      return await fn();
    } finally {
      unlock();
    }
  }
}

Mutex 2

export default class MutexC {
  private mutex = Promise.resolve();

  async run(fn) {
    return new Promise((resolve, reject) => {
      this.mutex = this.mutex.then(async () => {
        try {
          resolve(await fn());
        } catch (err) {
          reject(err);
        }
      });
    });
  }
}
...