Константа асинхронности для а) функций или б) строк кода?
join(user) {
if(this.room.length === 2) throw new Error('The room is full');
this.room.push(user);
};
Если асинхронность выполняется на основе "функции", этот код является на 100% пуленепробиваемым. В комнате не может быть более двух пользователей.
Если асинхронность выполняется по принципу "на строку", этот код может дать сбой. Зачем? Поскольку, если в комнату одновременно входят три пользователя, с интервалом в 10 мс может произойти следующее:
1 мс: комната пуста Впустите пользователя A, нажмите на массив.
4ms: Комната имеет одного пользователя.
5ms: пользователь B просит присоединиться.
6 мс: пользователь C просит присоединиться.
7ms: проверить массив на длину (1).
8ms: проверить массив на длину (1).
9 мс: нажмите пользователя B в массив, потому что длина комнаты равна 1.
10 мс: нажмите пользователя C в массив, потому что длина комнаты равна 1.
15 мсек: теперь в комнате 3 пользователя, а максимум - два.
Если асинхронность происходит по принципу "на строку", как мне избежать предыдущего примера в реальном сценарии? Извините, если я не назвал вещи по имени, но я не могу придумать лучшего способа объяснить это.
Комментарии предполагают (но не говорят наверняка и четко), что "экземпляры" одной и той же функции никогда не будут перекрывать друг друга. Как насчет различных функций, указывающих на один и тот же объект / массив?
join(user) {
if(GLOBAL.room1.length === 2) throw new Error('The room is full');
GLOBAL.room1.push(user);
};
join2(user) {
if(GLOBAL.room1.length === 2) throw new Error('The room is full');
GLOBAL.room1.push(user);
};
1 ответ
Javascript является синхронным для каждого события (большая область, чем функция или строка). Это исключает любые асинхронные операции, которые вы запускаете сами. Они начнут, а затем завершат через некоторое время свое собственное событие (см. Обсуждение событий позже здесь). Синхронный код для события (наподобие того, что вы показываете) будет полностью выполнен до завершения следующего события.
Ваш javascript в nodejs работает как однопоточный и управляемый событиями. Это означает, что один фрагмент Javascript начинает выполняться, он завершится до того, как будут обработаны любые другие события. Таким образом, функция запускается и завершается до того, как кто-либо может вызвать ее снова (при условии, что она не вызывает сама себя), поэтому у вас нет типичных типов состояний гонки, которые могут существовать в многопоточной среде.
Если асинхронность выполняется на основе "функции", этот код является на 100% пуленепробиваемым. В комнате не может быть более двух пользователей.
Javascript в node.js является однопоточным, и он будет запускать весь обработчик событий до завершения (все функции в этом обработчике событий) перед запуском следующего события.
Если асинхронность выполняется по принципу "на строку", этот код может дать сбой. Зачем? Поскольку, если в комнату одновременно входят три пользователя, с интервалом в 10 мс может произойти следующее:
Единственная многопоточность Javascript не для строки или для функции. Это за событие. Итак, в этом случае, похоже, вы обрабатываете входящее сообщение socket.io. Это означает, что весь код в этом обработчике события входящего сообщения будет выполняться до того, как ЛЮБЫЕ другие входящие сообщения могут быть обработаны. Только когда ваш обработчик событий вернет управление обратно в систему (вернувшись из своей функции обработчика событий), nodejs получит следующее событие в очереди событий и вызовет свой обработчик событий.
Итак, предположим, что все три пользователя просят присоединиться к одной комнате примерно в одно и то же время и изучить этот сценарий, чтобы объяснить, как это работает.
- Все три клиента просят присоединиться к одной комнате примерно в одно и то же время.
- Один из этих запросов поступает незначительно раньше, чем другой (входящие пакеты сериализуются по сетевому кабелю и во входящем стеке TCP, поэтому один из них поступает первым).
- Этот пакет обрабатывается локальным стеком TCP, а затем прослушивается сокет прослушивания в nodejs (это будет в собственном коде и выполняется в потоке, отличном от механизма JS).
- Код платформы nodejs получает это уведомление о сокете и вставляет событие в очередь событий nodejs, чтобы предупредить код nodejs, прослушивающий это входящее сообщение.
- Если движок Javascript nodejs в данный момент ничего не делает, он немедленно запускает этот обработчик событий и запускает код, связанный с этим обработчиком событий.
- Если движок nodejs Javascipt что-то делает в данный момент, то событие находится в очереди событий до тех пор, пока движок JS не завершит то, что он в данный момент делает, а затем извлечет следующее событие в очереди событий.
- Теперь два других запроса на присоединение к той же комнате поступают в стек TCP.
- Собственный код в nodejs, который обслуживает входящие TCP-пакеты, видит каждый из этих двух запросов. Они вставляются в очередь событий nodejs так же, как и первая. Поскольку интерпретатор JS занят обслуживанием первого поступившего сообщения, они пока просто находятся в очереди событий.
- Когда первое сообщение заканчивает свою обработку, интерпретатор nodejs получает следующее событие из очереди событий (в зависимости от того, какое сообщение пришло вторым, оно будет обработано сейчас). Будет вызван его обработчик события, и он будет завершен до обработки третьего события.
Надеемся, что из этого описания того, как все обрабатывается, вы можете видеть, что при проверке длины комнаты нет возможных условий гонки. Добавление пользователя в комнату является однопоточным, поэтому оно будет начинаться, прогрессировать и завершаться до того, как будут обработаны любые другие запросы на присоединение к комнате. Этот однопоточный характер (хотя иногда и ограничивающий признак) значительно упрощает программирование nodejs, поскольку устраняет многие из возможных причин состояний гонки, которые имеет многопоточное программирование.
Давайте посмотрим на вашу последовательность с моими комментариями на каждом шаге:
1 мс: комната пуста Впустите пользователя A, нажмите на массив.
4ms: Комната имеет одного пользователя.
5ms: пользователь B просит присоединиться.
Если первый запрос еще не завершен, этот запрос будет помещен в очередь событий до тех пор, пока не будет выполнен первый запрос. Если первый запрос выполнен, то этот запрос начнет обрабатываться.
6 мс: пользователь C просит присоединиться.
Предполагая, что для обработки второго запроса требуется более 1 мс, поэтому он еще не выполнен, этот запрос будет помещен в очередь событий и не будет обрабатываться, пока оба из первых двух запросов не будут полностью выполнены.
7ms: проверить массив на длину (1).
Я не понимаю, что это за шаг. Если это является частью обработки второго запроса, то второй и третий запросы все еще находятся в очереди событий и не могут выполняться, пока не будет выполнен первый.
8ms: проверить массив на длину (1).
То же, что и в предыдущем комментарии.
9 мс: нажмите пользователя B в массив, потому что длина комнаты равна 1.
10 мс: нажмите пользователя C в массив, потому что длина комнаты равна 1.
15 мсек: теперь в комнате 3 пользователя, а максимум - два.
Это ошибочная логика. Из-за очереди событий и однопоточного характера Javascript nodejs запросы user B и user C не выполняются, пока не будет выполнен запрос userA. Таким образом, длина комнаты всегда точна при выполнении любого запроса. Несколько подобных запросов не запускаются одновременно.