У меня есть функция под названием sendMail
, которая получает адрес электронной почты и отправляет письмо этим пользователям.
Эта функция вызывается нажатием кнопки в пользовательском интерфейсе.
Но если эта функция вызывается более одного раза, электронное письмо также отправляется более одного раза.
Я хотел бы заблокировать функцию, чтобы она не выполнялась снова, пока она уже запущена. Я имею в виду, что если пользователь вызвал ее, я хотел бы заблокировать кнопку, чтобы пользователь не щелкнул ее снова и снова не вызвал эту функцию.
Это маршрут для отправки почты
routes.get('/sendMail', (req, res)=>{
db.find({ subscribed: true}, (err, result) => {
if (err){
throw err;
}else{
result.forEach(function(subscriber, index){
setTimeout(function() {
sendMail(subscriber);
}, 3000*index)
});
res.render('records.ejs', {root: '.'});
}
});
});
Как только js является однопоточным и предположим, что я дважды вызываю sendMail
, эта функция должна выполняться снова только после завершения текущего выполнения?
Извините, если это вопрос новичка. Любые сообщения в блогах, учебники и т. д. приветствуются.
как функция вызывается более одного раза?
Вы звоните sendMail
каждые 3000*index
миллисекунд каждому члену result
. Какого поведения вы на самом деле хотите?
@ScottMarcus Я перефразирую свой вопрос. Я надеюсь, что это было ясно.
@SyedMehtabHassan Я перефразирую вопрос, чтобы объяснить его. Это вызов по нажатию кнопки. Таким образом, пользователь может дважды щелкнуть по нему и дважды вызвать функцию, например.
Как вы называете интерфейс формы '/sendMail'? добавьте свой интерфейсный код нажатия кнопки.
У вас может быть что-то вроде этого
processingOjb = {};
routes.get('/sendMail', (req, res)=>{
db.find({ subscribed: true}, (err, result) => {
if (err){
throw err;
}else{
result.forEach(function(subscriber, index){
if (!processingObj[subscriber.id]){
setTimeout(function() {
sendMail(subscriber);
delete processingObj[subscriber.id];
}, 3000*index)
}
processingObj[subscriber.id] = true;
}
});
res.render('records.ejs', {root: '.'});
}
});
});
Затем он не будет снова вызывать того же абонента, пока не будет разрешен. Просто будьте внимательны, чтобы справиться с тем, что происходит в случае ошибок, иначе вы можете получить подписчиков, которые никогда не будут удалены. Вы также можете поставить временную метку вместо true/false и принимать ее только в том случае, если она существует и не старше 10 минут (чтобы избежать навсегда блокирующего поведения пользователя).
Однако это не сработает, если у вас есть балансировщик нагрузки с несколькими экземплярами, и вы вызываете конечную точку несколько раз, поскольку ее могут использовать разные службы. В таком случае вам понадобится, например, Redis для такого поведения.
I'd like to block the button to prevent that the user click again and call this function again.
Один очень простой подход, который часто используется, состоит в том, чтобы просто отключить кнопку после того, как она была нажата. Вот пример:
// Get button reference and set up click handler
document.querySelector("button").addEventListener("click", function(){
// Here's where you'd call your sendMail function
// sendMail();
console.info("send mail has been called");
// Then disable the button:
this.disabled = true;
});
<button>Click Me</button>
Другое, более надежное решение — удалить обработчик событий после его вызова. Вы все еще хотите, чтобы кнопка была отключена для лучшего UX.
// Get button reference and set up click handler
document.querySelector("button").addEventListener("click", handleButtonClick);
function handleButtonClick(){
// Here's where you'd call your sendMail function
// sendMail();
console.info("send mail has been called");
// Then remove the handler and disable the button
this.removeEventListener("click", handleButtonClick);
this.disabled = true;
}
<button>Click Me</button>
Стоит отметить, что это не помешает другому клиенту вызвать конечную точку. Неясно, должен ли быть разрешен только один экземпляр процесса для каждого пользователя или универсально. Однако код не делает ничего конкретного для пользователя. Возможно, было бы лучше решить эту проблему на бэкэнде, переместив задание в запланированную фоновую службу. Вызов конечной точки просто поместит новое задание в очередь для выполнения этой задачи. Фоновая служба гарантирует, что новый запрос не будет обработан до тех пор, пока не завершится обработка предыдущего.
@ADyson ADyson В целом хорошие замечания, но OP действительно указывает Я хотел бы заблокировать кнопку, чтобы пользователь не щелкнул ее снова и снова не вызвал эту функцию., так что ответ адресован именно этому.
Я этого не отрицаю, ваш ответ вполне законен. Чтобы было ясно, я не столько критикую ответ, сколько размышляю, действительно ли ОП задает правильный вопрос или рассмотрел все возможности.
@ADyson Понятно. ;)
JavaScript является однопоточным. Две функции не могут работать одновременно, поэтому ваш вопрос кажется нечленораздельным. Можете ли вы перефразировать, чтобы объяснить, с какой реальной проблемой вы столкнулись?