В однопоточном, синхронном, нерекурсивном коде мы можем быть уверены, что для любой данной функции никогда не выполняется более одного вызова за раз.
Однако в мире async/await
вышесказанное больше не применяется: хотя мы ожидаем чего-то во время выполнения асинхронной функции f, она может быть вызвана снова.
Мне пришло в голову, что, используя отправители событий и очередь, мы можем написать обертку вокруг асинхронной функции, чтобы гарантировать, что она никогда не выполняла более одного вызова за раз. Как то так:
const events = require('events')
function locked(async_fn) {
const queue = [] // either actively running or waiting to run
const omega = new events()
omega.on('foo', () => {
if (queue.length > 0) {
queue[0].emit('bar')
}
})
return function(...args) {
return new Promise((resolve) => {
const alpha = new events()
queue.push(alpha)
alpha.on('bar', async () => {
resolve(await async_fn(...args))
queue.shift()
omega.emit('foo')
})
if (queue.length === 1) omega.emit('foo')
})
}
}
Идея состоит в том, что если f
является асинхронной функцией, то locked(f)
является функцией, которая делает то же самое, за исключением того, что если f
вызывается во время выполнения f
, новый вызов не начинается, пока возвращается первый вызов.
Я подозреваю, что у моего решения есть много возможностей для улучшения, поэтому я задаюсь вопросом: есть ли лучший способ сделать это? Фактически, он уже встроен в Node или доступен через npm?
РЕДАКТИРОВАТЬ, чтобы показать, как это используется:
async function f() {
console.log('f starts')
await new Promise(resolve => setTimeout(resolve, 1000))
console.log('f ends')
}
const g = locked(f)
for (let i = 0; i < 3; i++) {
g()
}
Выполнение этого занимает 3 секунды, и мы получаем следующий вывод:
f starts
f ends
f starts
f ends
f starts
f ends
Принимая во внимание, что если мы заменим g()
на f()
в цикле for
, выполнение займет 1 секунду и получит следующее:
f starts
f starts
f starts
f ends
f ends
f ends
(Я понимаю, что это довольно незначительный вопрос, и если он не подходит для стекового потока, я прошу прощения, но я не знал лучшего места для него.)