Как заблокировать цикл forEach во время его выполнения, чтобы он не выполнялся дважды?

У меня есть функция под названием 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, эта функция должна выполняться снова только после завершения текущего выполнения?

Извините, если это вопрос новичка. Любые сообщения в блогах, учебники и т. д. приветствуются.

JavaScript является однопоточным. Две функции не могут работать одновременно, поэтому ваш вопрос кажется нечленораздельным. Можете ли вы перефразировать, чтобы объяснить, с какой реальной проблемой вы столкнулись?

Scott Marcus 10.04.2019 12:54

как функция вызывается более одного раза?

Syed Mehtab Hassan 10.04.2019 12:54

Вы звоните sendMail каждые 3000*index миллисекунд каждому члену result. Какого поведения вы на самом деле хотите?

Scott Marcus 10.04.2019 12:55

@ScottMarcus Я перефразирую свой вопрос. Я надеюсь, что это было ясно.

mr.abdo 10.04.2019 13:01

@SyedMehtabHassan Я перефразирую вопрос, чтобы объяснить его. Это вызов по нажатию кнопки. Таким образом, пользователь может дважды щелкнуть по нему и дважды вызвать функцию, например.

mr.abdo 10.04.2019 13:01

Как вы называете интерфейс формы '/sendMail'? добавьте свой интерфейсный код нажатия кнопки.

Malik Adil 10.04.2019 13:11
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
6
128
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

У вас может быть что-то вроде этого

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 10.04.2019 13:56

@ADyson ADyson В целом хорошие замечания, но OP действительно указывает Я хотел бы заблокировать кнопку, чтобы пользователь не щелкнул ее снова и снова не вызвал эту функцию., так что ответ адресован именно этому.

Scott Marcus 10.04.2019 13:58

Я этого не отрицаю, ваш ответ вполне законен. Чтобы было ясно, я не столько критикую ответ, сколько размышляю, действительно ли ОП задает правильный вопрос или рассмотрел все возможности.

ADyson 10.04.2019 14:00

@ADyson Понятно. ;)

Scott Marcus 10.04.2019 14:02

Другие вопросы по теме