Являются ли JavaScript события l oop операциями по блокировке переменных? - PullRequest
1 голос
/ 02 апреля 2020

В неблокирующем событии l oop из JavaScript безопасно ли читать, а затем изменять переменную? Что произойдет, если два процесса захотят изменить переменную почти одновременно?

Пример A:

  • Процесс 1: Получить переменную A (это 100 )
  • Процесс 2: Получить переменную A (это 100)
  • Процесс 1: Добавить 1 (это 101)
  • Процесс 2: Добавить 1 (это 101)
  • Результат: переменная A равна 101 вместо 102

Вот упрощенный пример с маршрутом Express. Допустим, маршрут называется 1000 в секунду:

let counter = 0;

const getCounter = () => {
  return counter;
};

const setCounter = (newValue) => {
  counter = newValue;
};

app.get('/counter', (req, res) => {
  const currentValue = getCounter();
  const newValue     = currentValue + 1;
  setCounter(newValue);
});

Пример B:

Что если мы сделаем что-то более сложное, например Array.findIndex(), а затем Array.splice() ? Может ли быть так, что найденный индекс устарел, потому что другой событийный процесс уже изменил массив?

  • Процесс A findIndex (это 12000)
  • Процесс B findIndex (это 34000 )
  • Индекс сращивания процесса A 12000
  • Индекс сращивания процесса B 34000
  • Результат: процесс B удалил неправильный индекс, должен был удалить 33999 вместо
const veryLargeArray = [
  // ...
];

app.get('/remove', (req, res) => {
  const id = req.query.id;
  const i  = veryLargeArray.findIndex(val => val.id === id);
  veryLargeArray.splice(i, 1);
});

Пример C:

Что если мы добавим операцию asyn c в пример B?

const veryLargeArray = [
  // ...
];

app.get('/remove', (req, res) => {
  const id = req.query.id;
  const i  = veryLargeArray.findIndex(val => val.id === id);

  someAsyncFunction().then(() => {
    veryLargeArray.splice(i, 1);
  });
});

Этот вопрос было довольно трудно найти правильные слова, чтобы описать это. Пожалуйста, не стесняйтесь обновлять заголовок.

1 Ответ

1 голос
/ 03 апреля 2020

По ссылке @ ThisIsNoZaku, Javascript имеет принцип «Выполнить до завершения»:

Каждое сообщение обрабатывается полностью перед обработкой любого другого сообщения.

Это предложение некоторые приятные свойства при рассуждении о вашей программе, в том числе тот факт, что всякий раз, когда функция запускается, она не может быть перегружена и будет выполняться полностью до запуска любого другого кода (и может изменять данные, которыми манипулирует функция). Это отличается от C, например, когда если функция выполняется в потоке, она может быть остановлена ​​в любой момент времени системой выполнения для запуска другого кода в другом потоке.

Недостаток этой модели в том, что если сообщение занимает слишком много времени, веб-приложение не может обрабатывать взаимодействия с пользователем, такие как щелчок или прокрутка. Браузер смягчает это с помощью диалога «скрипт слишком долго запускается». Хорошая практика состоит в том, чтобы сделать обработку сообщений короткой и, если возможно, сократить одно сообщение на несколько сообщений.

Дальнейшее чтение: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

Итак , для:

Пример A: Это прекрасно работает в качестве узла сайта.

Пример B: Это также прекрасно работает, но если много запросов происходит одновременно, тогда последний отправленный запрос будет ждать некоторое время.

Пример C: Если другой вызов \remove будет отправлен до завершения someAsyncFunction, то вполне возможно, что ваш массив будет недействительным. Чтобы решить эту проблему, можно переместить поиск индекса в предложение .then функции asyn c.

IMO, за счет задержки это решает множество потенциально болезненных проблем параллелизма. Если вам нужно оптимизировать скорость запросов, я бы посоветовал взглянуть на различные архитектуры (дополнительное кэширование и т. Д. c).

...