Reputation: 373
On incoming webrtc call, I open a modal to show user a message about media permissions and await
this till the user presses OK button on the modal.
I do it like this:
async function myMessage(){
$('#id_my_message_modal').modal('show');
return new Promise(resolve =>
$('#id_my_message_button').on('click', () => {
$('#id_my_message_modal').modal('hide');
resolve();
}
)
);
}
then
await myMessage();
The issue I am facing now is if await myMessage();
is called again while the previous call has still not returned(i.e user hasn't pressed OK button). I want a way to cancel any previous await myMessage();
, if exists, before it is called again.
Is there any way to do it?
Upvotes: 0
Views: 301
Reputation: 1597
The first approach (Live demo)- add every call of the async function to the queue, so you will get a sequence of dialogs with the result returning (close/accept/whatever).
// decorator from my other answer
function asyncBottleneck(fn, concurrency = 1) {
const queue = [];
let pending = 0;
return async (...args) => {
if (pending === concurrency) {
await new Promise((resolve) => queue.push(resolve));
}
pending++;
return fn(...args).then((value) => {
pending--;
queue.length && queue.shift()();
return value;
});
};
}
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const myMessage = asyncBottleneck(async function () {
const $modal = $("#id_my_message_modal");
$modal.modal("show");
const result = await new Promise((resolve) =>
$modal.on("click", "button", (e) => {
$modal.modal("hide");
$modal.off("click", "button");
resolve($(e.target).data("action"));
})
);
await delay(250);
return result;
});
The second approach (Live demo)- multiplexing the fn calls, when every await of the function will return the same result; Compare console output of the live demos.
function singleThread(fn) {
let promise = null;
return function (...args) {
return (
promise ||
(promise = fn(...args).finally(() => {
promise = null;
}))
);
};
}
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const myMessage = singleThread(async function () {
const $modal = $("#id_my_message_modal");
$modal.modal("show");
const result = await new Promise((resolve) =>
$modal.on("click", "button", (e) => {
$modal.modal("hide");
$modal.off("click", "button");
resolve($(e.target).data("action"));
})
);
await delay(250);
return result;
});
$("#btn-run").on("click", async () => {
myMessage().then((c) => console.log(`First result: ${c}`));
await myMessage().then((c) => console.log(`Second result: ${c}`));
myMessage().then((c) => console.log(`Third result: ${c}`));
});
The third way- closing the previous modal with its promise rejecting (Live Demo open console there to see the result).
import CPromise from "c-promise2";
const showModal = (() => {
let prev;
return (id, text = "") => {
prev && prev.cancel();
return (prev = new CPromise((resolve, reject, { onCancel }) => {
const $modal = $(id);
text && $modal.find(".modal-body").text(text);
$modal.modal("show");
const dispose = () => {
$modal.modal("hide");
$modal.off("click", "button");
};
$modal.on("click", "button", function (e) {
dispose();
resolve($(this).data("action"));
});
$modal.on("hidden.bs.modal", () => {
setTimeout(() => resolve("close"));
});
onCancel(dispose);
})).finally(() => (prev = null));
};
})();
$("#btn-run").on("click", async () => {
showModal("#id_my_message_modal", "First message").then(
(c) => console.log(`First modal result: ${c}`),
(e) => console.warn(`First modal fail: ${e}`)
);
showModal("#id_my_message_modal", "Second message").then(
(c) => console.log(`Second modal result: ${c}`),
(e) => console.warn(`Second modal fail: ${e}`)
);
const promise = showModal("#id_my_message_modal", "Third message")
.then(
(c) => console.log(`Third modal result: ${c}`),
(e) => console.warn(`Third modal fail: ${e}`)
)
.timeout(5000)
.then(() => {
return showModal(
"#id_my_message_modal2",
"Blue Pill or Red Pill?"
).then((v) => console.log(`Pill: ${v}`));
});
/*setTimeout(()=>{
promise.cancel(); you can cancel the modal from your code
}, 1000);*/
});
Upvotes: 1