The Situation
I have an asynchronous function that takes ~X seconds to resolve. The duration depends on network latency so it is unpredictable. The function is triggered by user input (think "save current form values to the server")
I would like to prevent this function from running multiple times simultaneously.
I would also like to "queue" calls to the function for potentially calling when the time is right.
The Question
(A) Is there a term for this concept of a "patient" async function.
(B) Is there a tool (similar to debounce) that will take an async function and turn it into a "patient" one.
CodePudding user response:
This answer is based on the answer by @danh.
The function queuePromise takes a function that returns a promise. Whenever the queued function is called, it's promise is chained to the promise of the previous call.
The main difference between @danh's answer and this, is that the queued function can receive arguments, and the promise is a simple let variable.
const queuePromise = fn => {
let pending = Promise.resolve()
return (...args) => {
const func = () => fn(...args)
return pending = pending.then(func, func)
}
}
const demo = name => id => {
console.log(`started ${name} ${id}`)
return new Promise((resolve, reject) => {
setTimeout(() => {
// example of a failed promise in the middle of the chain
if(id === 2) reject(`failed ${name} ${id}`)
else resolve(`ended ${name} ${id}`)
}, 500)
})
}
const patientFoo = queuePromise(demo('foo'))
patientFoo(1).then(console.log).catch(console.log)
patientFoo(2).then(console.log).catch(console.log)
patientFoo(3).then(console.log).catch(console.log)
const patientBar = queuePromise(demo('bar'))
patientBar(1).then(console.log).catch(console.log)
patientBar(2).then(console.log).catch(console.log)
patientBar(3).then(console.log).catch(console.log)
CodePudding user response:
Below, makePatient wraps a given function in another that invokes it only after the chain of prior invocations is resolved.
function pause() {
return new Promise(resolve => {
setTimeout(resolve, 1000)
})
}
function foo(str) {
return pause().then(() => {
if (str !== 'foo') throw 'what the foo?';
console.log(str);
});
}
function bar(str) {
return pause().then(() => console.log(str));
}
function makePatient(fn) {
let pending = Promise.resolve();
return (...params) => {
return pending = pending.then(() => fn(...params)).catch(console.log);
}
}
let patientFoo = makePatient(foo);
let patientBar = makePatient(bar);
patientFoo('foo');
patientFoo('not foo').catch(e => console.log)
patientFoo('foo');
patientFoo('foo');
patientBar('bar');
patientBar('bar');
patientBar('bar').finally(() => console.log("all the bars are closed"));
