Я нашел где-то реализацию шаблона мьютекса в 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);
}
});
});
}
}