LINUX.ORG.RU

Можно ли сделать главный коллбек асинхронным?

 , ,


0

1

Прошу прощения, давно уже не программировал, а тут понадобилось, может что-то упустил из новшеств. Помню, делал я сервера на nodejs, и, насколько я помню, там если в главном коллбеке пишешь синхронный код, выполнение блокируется.

Сейчас проверил, на всякий случай, на таком вот коде:

#!/usr/bin/nodejs




http = require("http")

flag = true

http.Server(function(req, res){
   if(req.url === "/favicon.ico") {
      console.log("favicon")
      res.end()
      return
   }
   var i = 10000000000
   if(flag){
     console.log("flag is true")
     flag = false
     console.log("flag setted to false")
     while(i--) {}
     console.log("cycle is done")
   } else {
     console.log("flag is false")
   }
   res.write("foo")
   res.end()
}).listen(8888)

При обращении первого клиента флаг установлен в true, выполняется цикл, при обращении второго цикл не выполняется, поэтому, если бы цикл не блокировал, то второй клиент получал бы ответ сразу, но он ждет. В консоли пишет

flag is true
flag setted to false
cycle is done
flag is false
favicon
favicon
Это подтверждает: если бы коллбек сработал асинхронно, то во время выполнения цикла, второй запрос был бы обработан, соответственно, 4-я строка была бы перед cycle is done, то есть она была бы третьей в логе.

В общем, я подумал, что было бы намного проще и логичней, если бы этот коллбек был асинхронным сам по себе. Почему они так не сделали? Чтобы добавить асинхронность, нужно внутри этого синхронного коллбека городить еще асинхронный сверху, а это запутывает код.

Может эта фича есть, и я просто не знаю об этом?



Последнее исправление: asyncron2018 (всего исправлений: 3)

Асинхронность - это не много поточность.

surefire ★★★
()

Массивные вычисления можно разбить на части и переносить каждую следующую часть на следующий event loop, но это всё равно не очень хорошо. Хорошо - выносить в другой в внешний поток, процесс, сервер, кластер.

surefire ★★★
()
Последнее исправление: surefire (всего исправлений: 1)
Ответ на: комментарий от surefire

В принципе, все равно не совсем ясно, ведь nodejs своими коллбеками порождает отдельные процессы, например чтение с диска, так что же мешало сделать так, чтобы она порождала собственные процессы

asyncron2018
() автор топика
Ответ на: комментарий от asyncron2018

Тогда нода не была бы такой быстрой. Направление движения везде — замена процессов на потоки, виртуалки на контейнеры и прочее облегчение всех программных оберток. Асинхронная работа это тоже способ избегать накладных расходов времени исполнения.

Jaberwock ★★★
()
Ответ на: комментарий от asyncron2018

ведь nodejs своими коллбеками порождает отдельные процессы, например чтение с диска

Нет.

surefire ★★★
()

Все коллбеки выполняются в отдельном потоке. А теперь перепиши на тайм аут и увидишь разницу. Смысл в том, что ничего там не блокируется, ты че то не то понял, всё работает асинхронно, но ты своими флагами просто заставляешь node менять flow потока выполнения на какую-то дичь. Тебе зачем эта муть? Возьми express.js и попробуй его заблокировать своими циклами =)

menangen ★★★★★
()
Ответ на: комментарий от asyncron2018

Node.js состоит из нескольких «либ»: uv для ввода-вывода, v8 для обработки и выполнения js. Посмотри на вики че к чему по поводу этих либ, и как они работают, и всё станет понятно.

menangen ★★★★★
()

Эмм. Нода однопоточна, поэтому твой код будет блокировать этот единственный поток.

Хочешь неоднопоточную? Тогда:

  • Это будет медленнее
  • В каком месте форкать поток? По потоку на запрос?
  • Как синхронизировать общий стейт? У тебя же есть тот же flag, если ты меняешь его из двух потоков — покажи, где ты собираешься поставить lock/что-там ещё? Ты же не собираешься сделать race condition на ровном месте?

С процессами все те же проблемы + нужно как-то шарить стейт (shared memory для экстремалов, сериализовать и перекидываться им для тормозов).

Но вообще — если хочешь по потоку/процессу на запрос — есть тысячи не-нодовых фреймворков. В них даже callback-hell нет именно благодаря этому.

x3al ★★★★★
()
Последнее исправление: x3al (всего исправлений: 1)
Ответ на: комментарий от x3al

Да не однопоточна она, че вы за бред несёте. Была бы однопоточна, ни о какой асинхроннсти речи бы не шло. Другой вопрос, что рожать поток она может только на оперциях ввода-вывода, но не на оперциях доступа к внутренней памяти, что породило бы кучу проблем с синхронизацией потоков / scope js.

menangen ★★★★★
()
Ответ на: комментарий от menangen

Тут пишут

Сейчас бы в 2018 до сих пор не знать как устроена модель управления потоком выполнения в js-like средах.

Пользовательский код всегда выполняется в одном потоке.

В его случае второй клиент ждет, потому что он блокирует основной поток циклом (пользовательским кодом). Колбэк от второго клиента просто не может выполниться, пока основной поток, в котором он должен выполниться, заблокирован.

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

Вебворкеры в браузерах реализованы выполняются в раздельных потоках. Общение между ними происходит посылкой сообщения. Иожно передавать между ними куски памяти без копирования в памяти (передача владения). Есть аналогичная либа для ноды, которая тоже работает на потоках, однако же стандартное апи ноды предлагает лишь мультипроцессность.

anonymous
()
Ответ на: комментарий от menangen

Это и называется однопоточностью.

x3al ★★★★★
()
Ответ на: комментарий от anonymous

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

I/O обеспечивает OS и результат обрабатывается с помощью select, poll, epoll и тому подобного, для этого не нужны потоки, это все вотчина even loop. Кому нужен поток тот его создает сам с помощью доступных API всяких вебворкеров, нативных аддонов и так далее. Но это не имеет никакого отношения к I/O.

surefire ★★★
()
Ответ на: комментарий от surefire

Ты необучаемый, да?

var start = process.hrtime();

for (var i = 0; i < 100; ++i) {
    (function (id) {
        fs.readdir('.', function () {
            var end = process.hrtime(start);
            console.log(util.format('readdir %d finished in %ds', id, end[0] + end[1] / 1e9));
        });
    })(i);
}

output:

readdir 0 finished in 0.91778674s
readdir 1 finished in 0.917931036s
readdir 2 finished in 0.918161968s
readdir 3 finished in 0.918362412s
readdir 4 finished in 0.918560139s
readdir 6 finished in 0.918754243s
readdir 5 finished in 0.918948951s
readdir 7 finished in 0.919144263s
readdir 8 finished in 0.919341989s
readdir 9 finished in 0.919536094s
readdir 10 finished in 0.919731405s
readdir 11 finished in 0.919924604s
...
readdir 89 finished in 0.932873742s
readdir 90 finished in 0.933043696s
readdir 91 finished in 0.933208519s
readdir 92 finished in 0.93337636s
readdir 93 finished in 0.933545711s
readdir 94 finished in 0.933712646s
readdir 95 finished in 0.933880186s
readdir 96 finished in 0.934048329s
readdir 97 finished in 0.934215265s
readdir 98 finished in 0.934380993s
readdir 99 finished in 0.934569361s

process.env.UV_THREADPOOL_SIZE = 32

var start = process.hrtime();

for (var i = 0; i < 100; ++i) {
    (function (id) {
        fs.readdir('.', function () {
            var end = process.hrtime(start);
            console.log(util.format('readdir %d finished in %ds', id, end[0] + end[1] / 1e9));
        });
    })(i);
}

output:

readdir 2 finished in 1.649030852s
readdir 0 finished in 1.649261181s
readdir 1 finished in 1.649458002s
readdir 3 finished in 1.6496436540000001s
readdir 4 finished in 1.649830815s
readdir 5 finished in 1.650015259s
readdir 6 finished in 1.650193968s
readdir 7 finished in 1.650365734s
readdir 8 finished in 1.650536896s
readdir 9 finished in 1.6507071519999998s
readdir 10 finished in 1.650877408s
readdir 12 finished in 1.6510479660000001s
readdir 11 finished in 1.6512182229999999s
...
readdir 86 finished in 1.663928579s
readdir 87 finished in 1.664094307s
readdir 89 finished in 1.664260941s
readdir 88 finished in 1.664432405s
readdir 90 finished in 1.664605076s
readdir 91 finished in 1.6647702s
readdir 92 finished in 1.664937136s
readdir 93 finished in 1.665102562s
readdir 94 finished in 1.6652682909999998s
readdir 95 finished in 1.6654358299999998s
readdir 96 finished in 1.665603671s
readdir 97 finished in 1.665770003s
readdir 98 finished in 1.66593392s
readdir 99 finished in 1.6661014600000001s

anonymous
()
Ответ на: комментарий от surefire

Может в libev работа с файлами через nonblock, но в libuv через тредпул. Потомучто «Regular files are always readable and they are also always writeable. This is clearly stated in the relevant POSIX specifications. I cannot stress this enough. Putting a regular file in non-blocking has ABSOLUTELY no effects other than changing one bit in the file flags»

Пруф: http://docs.libuv.org/en/v1.x/design.html

makoven ★★★★★
()
Ответ на: комментарий от makoven

Не сочиняй. Умный книжки иди читать лучше. Что вы мне тут документациями каких-то конкретных обсуждаемых библиотек тычете. Мне удобнее натягивать вычитанные мною в умных книжках концепции на все подряд. Я же не дурак, в конце-то концов, какой-нибудь.

anonymous
()
Ответ на: комментарий от anonymous

Разработчики libuv, не выдержав несовершенство POSIX, взяли тредпул и натянули его на тормозную однопоточную парадигму eventloop,

Наверное поэтому Диджей Головайчик свалил с ноды на Го, как только последний вылупился

makoven ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.