Если в первый момент идея не кажется абсурдной, она безнадёжна.
— Альберт Эйнштейн

Мы собрали для вас самые популярные темы из обсуждений Node.js на Хабре, и попросили рассказать о них признанных экспертов: некоммерческого Node-хакера Матиаса Мэдсена и автора множества книг и курсов по Node, Азата Мардана.


Вот точный список тем:


  1. Потоки в Node.js и способы распараллеливания вычислений;
  2. Асинхронность в Node.js;
  3. Отладка и логирование в Node.js;
  4. Проблемы мониторинга производительности на продакшене;
  5. Инструменты для мониторинга нод.


    Азат Мардан (Azat Mardan) — Tech Fellow, менеджер в компании Capital One, и эксперт по JavaScript/Node.js с несколькими онлайн-курсами на Udemy и в Node University, а также автор 14 книг по той же тематике, включая «React Quickly» (Manning, 2017), «Full Stack JavaScript» (Apress, 2015), «Practical Node.js» (Apress, 2014) и «Pro Express.js» (Apress, 2014).







В свое свободное время Азат пишет о технологиях на Webapplog.com, выступает на конференциях и вносит свой вклад в open-source. Прежде чем стать экспертом в Node.js, Азат закончил магистратуру в области информационных систем, и поработал в федеральных государственных учреждениях США, небольших стартапах, и в крупных корпорациях с различными технологиями, такими как Java, SQL, PHP, Ruby и т. д.


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


Матиас Буус Мэдсен (Mathias Buus Madsen) — является некоммерческим хакером Node.js, базирующимся в Копенгагене, Дания. Он работает полный рабочий день с открытым исходным кодом и в проекте Dat (http://dat-data.com), создавая открытые инструменты, позволяющие ученым совместно использовать наборы данных. В настоящее время он поддерживает более 400 модулей на npm, что уже само по себе впечатляет.

Напоминаем, что встретиться с ними вживую можно на конференции HolyJS 2017 Moscow.


Появление на сцене серверного JavaScript надолго разделило программистское сообщество на тех, кто его принял, и всех остальных… Нам с вами не привыкать к холиварам, особенно, когда речь заходит о чем-то очевидном, вроде мысли, что сайты отлично пишутся на PHP (хм..., или на Perl? или на Python?.. впрочем, неважно, пост-то о Node.js), нам куда интереснее будет обсудить, не на чем писать, а как из этого, единственно верного хорошо подходящего языка/стека получить достойные результаты. Тем более что Node развивается, комьюнити ширится, версии появляются, серверный движок только совершенствуется, и приход светлого завтра (на фоне серого вчера) — как минимум не за горами!  Посмотрим, что скажут эксперты…


Азат


(я отвечаю на вопросы по состоянию дел на конец 2017 года, что означает Node 8, npm 5, и так далее; сегодня кое-что изменилось по сравнению с ранними днями Node, а что-то осталось более-менее тем же).


1. Потоки в Node.js и способы распараллеливания вычислений


Как многие знают, Node однопоточна; в этом и сильная, и слабая стороны Node. Сильная, потому что так проще реализовать асинхронный неблокирующий код, который позволит вашим системам выполнять больше операций ввода-вывода, что обычно означает обработку большего трафика. Слабая из-за того, что вы можете написать код, который будет блокироваться. Вам поможет инициирование нескольких потоков. В ядре языка существует модуль cluster, но большинство разработчиков Node используют pm2. Он поддерживает разработку (pm2-dev) и контейнеры (pm2-docker). Чтобы начать работу с pm2, просто установите его с помощью npm и запустите в фоновом режиме:


npm i -g pm2
pm2 start server.js -i 0

Если pm2 не подходит по все ваши требования, и вам все равно нужно работать на более низком уровне, вы можете использовать cluster. В новых версиях Node (текущая версия 8) у него есть балансировка нагрузки, как в pm2. В результате, несколько ваших процессов смогут слушать на одном и том же порту, и смогут взаимодействовать друг с другом и с основным процессом. Вам следует использовать fork() с cluster. Вот хороший пример:


const cluster = require('cluster')
const numCPUs = require('os').cpus().length
if (cluster.isMaster) {
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork()
  }
} else if (cluster.isWorker) {
  // your server code
})

Наконец, есть два метода для ручного создания процессов в дополнение к fork(): spawn() и exec(). Первый — для длительных процессов, потоковой передачи и большого объема данных, в то время как второй подходит для небольшого вывода данных.


2. Асинхронное программирование в Node.js


Да, в Node есть async/await функции. Мне все еще нравятся колбеки, но асинхронные функции прекрасны. Их легко понять даже новичкам. Код async функции короче, чем код promise. Взгляните на этот тест Mocha с двумя вложенными async вызовами к БД. Он короткий и симпатичный:


describe('#find()', () => {
    it('responds with matching records', async () => {
        const users = await db.users.find({
            type: 'User'
        })
        expect(users).to.have.length(3)
        for (let user of users) {
            const comments = await db.comments.find({
                user: user.id
            })
            expect(comments).to.be.ok
        }
    })
})

Кстати, вы можете использовать и синтаксис function() {}, не только синтаксис со стрелками:


const async function() {
    const {response: res} = await axios. get('https://webapplog.com/api/cupcakes')
}

Еще один бонус async функций заключается в том, что они совместимы с promise-ами. Да, все верно. Вы можете использовать их вместе, например, создавать async функцию, затем использовать then, или использовать библиотеку на основе promise (например, axios из моего примера), или функцию, созданную util.promisify() (новый метод Node 8!) как async функцию.


3. Отладка в Node.js


Отладчик в Node значительно улучшился в сравнении с тем, что было раньше. Я помню времена, когда я работал в Storify (компании — одном из самых первых пользователей Node), я просто размещал console.log по всему коду. Сегодня вы можете отлаживать в VS Code. Это потрясающий редактор. Я использую его каждый день.


Далее, существует Node Inspector, который, по сути, Chrome DevTools для программ на Node.js. В Node v8 появился Google Chrome V8 Inspector, интегрированный в Node, и все, что вам нужно сделать для начала работы с GUI-дебагером, это просто написать:


node --inspect index.js

затем открыть§chrome://inspect/#devices в браузере Chrome. В версии 7, нужно скопировать URL и открыть его в браузере Chrome. URL будет содержать в начале строку chrome-devtools://. Просто помните, что ваш скрипт на Node должен работать достаточно долго, чтобы отладчик из DevTools успел подключиться к программе, или вам придется расставлять брекпоиты в отладчике или в коде.


Node построен на Chrome V8 и использует Chrome DevTools для отладки, не просто потому, что там есть приятный GUI, но и чтобы обеспечить надежную работу функций и в будущем.


4. Проблемы производительности Node.js


Большинство проблем с производством Node связаны либо с утечками памяти, либо с сетью, либо с проблемами ввода-вывода. Стресс-тестирование поможет вам понять, как ваше приложение и система работают в условиях реальной нагрузки. Хороший инструмент — artillery. Некоторые проблемы с утечкой памяти могут быть устранены или смягчены путем незначительного изменения кода и использования свежей версии Node, которая идет с новым компилятором JIT JavaScript под названием Turbofan. Прочитайте этот отличный пост GET READY: НОВЫЙ V8 ПРИХОДИТ, ПРОИЗВОДИТЕЛЬНОСТЬ NODE.JS МЕНЯЕТСЯ по поводу оптимизации, а также техник, и кода, которые стоило бы либо избегать, либо взять на вооружение.


5. Хорошие инструменты для мониторинга Node.js


Для начала, убедитесь, что код Node готов к работе. В 2018 году это будет означать использование контейнеров, облаков и методов автоматизации. Возможно, вы захотите посмотреть мой курс Node in Production для получения более подробной информации.


Node должен масштабироваться как по вертикали (см. пункт 1), так и по горизонтали. Это, конечно же, затрудняет мониторинг и сбор логов. Вам необходимо будет собирать метрики и журналы.


Создайте сами простой дашбоард, и вы увидите статистику и метрики, которые формируют отдельные серверы и процессы Node… или используйте опен-сорсный дашбоард, скажем, Hygieia, созданный в Capital One (на случай: я работаю в Capital One).


Хорошими инструментами для работы с логами будут winston и bunyan. Вы можете отправлять журналы в любое место, скажем, в сторонний SaaS, например Loggly, Splunk или Papertail (мы использовали его в Storify). Если вы хотите держать все данные у себя, то разверните Elastic Search с Kibana, и отправьте свои журналы туда. Именно это, мы сделали в DocuSign, когда мы не смогли использовать сторонний сервис по соображениям безопасности и конфиденциальности. Мы разработали собственное решение на базе Winston, Elastic и Kibana.


Еще несколько инструментов и услуг, которые стоит учитывать (не все бесплатны), особенно для мониторинга в продакшене: это N|Solid, NewRelic и AWS CloudWatch.


Завершение


Node растет сумасшедшими темпами. Даже без новых версий это уже потрясающая технология. Она быстрая, надежная, и, самое главное, приносит разработчикам радость беззаботного кодирование. При перемещении в стек Node я видел много счастливых разработчиков Java и C#.


Матиас


Делаем несколько дел параллельно


Node.js по своему дизайну однопоточная. Это на самом деле полезная фича, поскольку это означает, что будет проще разбираться с вещами вроде состояний гонки по памяти, чем в языках вроде Java, где исполнение вашей программы может оказаться прервано в любое время.


Она может позволить себе быть однопотоковой, поскольку все операции ввода/вывода выполняются асинхронно, и, следовательно, не блокируют выполнение программы. Более того, хотя JavaScript в Node имеет один поток исполнения для программы, сам он использует для себя еще несколько потоков, чтобы справляться с помочь с фоновыми I/O задачами и другими служебными задачами.


Этот подход имеет только один недостаток. Порой вам нужно запустить код, который потребляет исключительно вычислительную мощность процессора. Поскольку в этот момент не производится операций ввода-вывода, ваша Node.js-программа блокируется до завершения этого кода. Если речь идет об интенсивно потребляющей процессор операции (например, криптографии), это может дать отрицательный эффект, поскольку ваша Node-программа не сможет ничего сделать, пока операция не завершится — вы, наверняка, захотите подобного избежать.


Существует несколько способов достичь этого. Один из таких подходов (который я часто использую) – написать нативный модуль (как правило, все мои процессорно-интенсивные операции все равно требуют использования какого-либо нативного модуля) и использовать действительно симпатичный worker api. При помощи этого API ваша (в прошлом — синхронная) операция сможет использовать колбек, либо вернуть promise, а ваша задача, созданная с использованием C++ и Worker API, будет работать в другом системном потоке исполнения.


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


В любом случае, когда вы начинаете реализовывать параллельность исполнения кода Node программы, вы получаете куда более асинхронный код. Управление асинхронным кодом в Node — одна из тех вещей, которые кажутся очень трудными поначалу, но становятся куда более простыми в понимании, по мере роста опыта. В текущей версии Node вы можете использовать возможности вроде async/await, чтобы ваш асинхронный код выглядел синхронным, если вы также используете promise. Одна сложность, вытекающая из этого — поскольку вы вынуждены использовать синтаксис try-catch, вам придется разбираться в т.ч. и с более серьезными багами, поскольку try-catch в JavaScript так же перехватывает ситуации, которые в других языках привели бы к ошибками компиляции (например, опечатки в коде и пр.). Другими словами, поиск багов станет более сложным, т. к. вы в коде обработки ошибок получите сообщения и о синтаксических ошибках, и о ошибках при выполнении программы.


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


Отладка и мониторинг


Одна из нравящихся мне черт Node — количество прекрасных средств, которые помогают вам отлаживать и мониторить поведение приложения (да я и сам написал их немало). Когда мы работаем с Node, мы работаем с кодом на JavaScript, который будет выполняться на V8. В свою очередь, V8 имеем богатые функционалом API, позволяющие выжать из него прекрасную производительность. Это позволяет вам отследить действительную причину проблем (то самое «бутылочное горлышко») в вашем приложении без каких-либо лишних угадываний.
Я особенно люблю модуль 0x (https://github.com/davidmarkclements/0x), написанный Девидом Марком Клемнентом и его друзьями. Этот модуль легко превращает бенчмарк в настоящий flamegraph. Просто запускаем ваш бенчмарк с 0x вместо node (плюс создаем flamegraph, и затем открываем его в браузере):


0x -o my-benchmark.js

Этот график четко расскажет, какая JavaScript функция использует при выполнении программы большее время процессора. Такой подход дает наглядное представление, на что обратить при оптимизации наибольшее внимание.


Другой модуль из тех, что я часто использую для ops-задач, мой собственный модуль под названием respawn. Он просто помогает вам запустить процесс, а затем перезапустит его, если процесс по-crash-ится.


К respawn есть симпатичная cli-обертка под названием lil-pids. lil-pids не имеет интерфейса, и требует один лишь файл с названием ./services: вы просто указываете в нем все команды, которые бы хотели видеть запущенными в вашей системе, lil-pids приглядывает за ними, и старается при помощи модуля respawn добиться, чтобы все они были запущены.


Наконец, еще проблема, которую мне чаще всего приходится решать в Node при эксплуатации кода в продакшене — это случайные утечки памяти. Даже несмотря на то, что JavaScript имеет свой сборщик мусора, мы часто допускаем утечки памяти, скажем, добавляя элементы в список, и забывая удалить их оттуда, и т.п. Иногда мы не допускаем утечку памяти, но реализуем алгоритмы, потребляющие столь много памяти, что в какой-то момент система вынуждена остановить программу программу. Для определения ситуации, когда имеет место утечка памяти, я довольно часто применяю модуль Томаса Вотсона под названием memory-usage. Единственная вещь, которую он делает — отдает вам бесконечный поток данных о том, как много памяти использует ваша программа во времени. Если нарисовать график этой величины, вы увидите, когда начинается утечка памяти.


Что бы ни говорила известная пословица, есть вариант лучший, чем «один раз увидеть» — это, в нашем случае, «увидеть и услышать доклады, а потом задать вопросы и пообщаться в кулуарах». Приглашаем на конференцию HolyJS 2017 Moscow!

Комментарии (53)


  1. BeppeGrillo
    27.10.2017 11:31
    +1

    Код async функции короче, чем код promise. Взгляните на этот тест Mocha с двумя вложенными async вызовами к БД. Он короткий и симпатичный:

    Он длинный и уродливый


    1. Odrin
      27.10.2017 11:34
      +2

      Может перепишите этот код так, что бы он был короче и более читаемым?


      1. vintage
        27.10.2017 13:09
        -1

         tests({
        
            'responds with matching records'() {
        
                const users = db.users.find({ type: 'User' })
                assert( users.length , 3 )
        
                for( let user of users ) db.comments.find({ user: user.id })
        
            }
        
        })

        Но вообще это какой-то слишком хрупкий тест. Где создание базы? Где заполнение? Где адекватная проверка возвращаемых значений?


        1. mayorovp
          27.10.2017 13:21
          +1

          Тут принципиальная разница — только в стиле: были литералы в три строчки, стали в одну.


        1. BeppeGrillo
          27.10.2017 13:38

          Вот, вот, уважаю!

          Odrin

          Может перепишите этот код так, что бы он был короче и более читаемым?

          Вон сверху уже за меня сделали.

          mayorovp
          Тут принципиальная разница — только в стиле: были литералы в три строчки, стали в одну.

          Отсутствие await и прочей лапши не заметно?

          Aries_ua
          А можете более детально обосновать, почему он длинный и уродливый?

          Потому что куча лапши а async/await создан не для того чтобы пхать его в каждую строчку типа «Смотрите как я могу», вон сверху имплементация от которой не хочется выколоть себе глаза.


          1. mayorovp
            27.10.2017 13:47

            Поясните ваше понимание термина "лапша".


          1. Druu
            27.10.2017 14:26
            +1

            > вон сверху имплементация от которой не хочется выколоть себе глаза

            Только она работает синхронно (или вообще не работает).


            1. vintage
              27.10.2017 14:49

              Вам сюда: https://habrahabr.ru/post/307288/#voloknahttpsgithubcomnin-jinasync-jscomparesyncasync-fibers
              Я устал уже про это рассказывать.


              1. faiwer
                27.10.2017 19:38

                Помниться к вам в комментарии разработчик nodeJS приходил. Кажется он разбил в пух и прав эти волокна с точки зрения производительности и вообще применимости их в реальном мире. С тех пор что-то поменялось?


                1. vintage
                  27.10.2017 19:56
                  -3

                  Не помню, чтобы ко мне кто-то приходил и что-то разбивал.


                  1. faiwer
                    27.10.2017 20:01
                    +4

                    А что так? Вот же оно. Там даже про segfault-ы речь шла.


                    1. vintage
                      27.10.2017 21:29
                      -1

                      1. mayorovp
                        27.10.2017 22:30

                        Нет, это вам пора туда заглянуть.


              1. faiwer
                27.10.2017 20:08
                +2

                Regard async+await, for new projects I think it's the right choice to use those new language features over Fibers. Future.fromPromise and future.promise() were specifically added to aid migration. I started fibers over 5 years ago back when generators were barely on the drawing board and I couldn't wait for ECMAScript to catch up.

                Комментарий автора, который вы должны хорошо помнить. История коммитов тоже не внушает доверия в пользу выбора fibers.


              1. Druu
                28.10.2017 07:09

                Но в вашем коде файберов нет, у вас просто обычный синхронный код.


                1. vintage
                  28.10.2017 08:46

                  В этом и вся соль волокон. Ознакомьтесь с ними по внимательнее — это классная штука.


                  1. Druu
                    28.10.2017 09:45

                    > В этом и вся соль волокон.

                    Соль волокон в _явной_ передаче управления. У вас ее нет, и волокон, соответственно, нет. Перепишите код с волокнами, тогда и будем сравнивать.


                    1. vintage
                      28.10.2017 10:56

                      Прочитайте статью внимательно и не говорите глупостей.


                      Остальной код выглядит примерно так:


                      function tests( cases ) {
                          for( let name in cases ) cases[ name ]()
                      }

                      Fiber( tests ).run()

                      const db = {
                          users : {
                              find : params => {
                                  const future = new Future
                                  setTimeout( ()=> {
                                      future.return([ { id : 1 , name : 'Jin' } ])
                                  } , 1000 )
                                  return future.wait()
                              }
                          }
                      }


                      1. Druu
                        29.10.2017 11:56

                        И как теперь, например, сделать два find-запроса параллельно?


                        1. vintage
                          29.10.2017 14:07

                          const db = {
                              users : {
                                  find : params => {
                                      const future = new Future
                                      setTimeout( ()=> {
                                          future.return([ { id : 1 , name : 'Jin' } ])
                                      } , 1000 )
                                      return new Proxy( [] , { get : ()=> future.wait() } )
                                  }
                              }
                          }


                          1. Druu
                            29.10.2017 15:27

                            Вы не поняли. Я говорю о том, что у вас есть одна find, и я хочу сперва сделать пару запросов последовательно, а потом — параллельно.


                            1. vintage
                              29.10.2017 16:36

                              Параллельно:


                              const jins = db.users.find({ name : 'Jin' })
                              const nins = db.users.find({ name : 'Nin' })
                              console.log( jins , nins )

                              Последовательно:


                              const user = db.users.find({ name : 'Jin' })[0]
                              const coments = db.comments.find({ author : user.id })
                              console.log( user , comments )

                              Ну и последовательно, на случай неявной зависимости:


                              db.users.populate({ count : 3 , comments : 10 }).valueOf()
                              const users = db.users.find({ type : 'User' })
                              console.log( users )


                  1. redyuf
                    28.10.2017 11:15

                    Идейно может это и классная штука.
                    Но с виду проект полудохлый, активность низкая, та issue с Maximum call stack (229) висит второй год и перечеркивает продакшен использование волокон.

                    Упоминая полудохлую технологию, работающую только на ноде и которую вряд ли когда примут в стандарт, что вы пытаетесь показать? Что она удобнее async/await, ну может быть, но знание этого ничего не дает — оно бесполезно.


                    1. vintage
                      29.10.2017 02:56

                      22 724 downloads in the last day
                      155 756 downloads in the last week
                      663 430 downloads in the last month


                      Из широко известных проектов: meteor и apollo-server


                      Открытых багов 5
                      Закрытых — 296


                      Падение в той задаче воспроизводится в весьма специфических условиях: arch linux + ожидание одновременно нереалистично большого числа задач.


                      1. redyuf
                        29.10.2017 12:56
                        +1

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

                        За 1.5 года никто не перепроверил? Там уж несколько версий ядер сменилось и самого арча, автору пофиг?

                        Просто отталкивают такие вещи от использования.



                      1. Odrin
                        30.10.2017 10:26
                        +1

                        apollo-server

                        В одном единственном тестовом файле. И только для того, что бы:
                        to simulate a Meteor environment


        1. Druu
          27.10.2017 14:32

          Uncaught TypeError: users is not iterable


    1. Aries_ua
      27.10.2017 12:39
      +1

      А можете более детально обосновать, почему он длинный и уродливый? Хотелось бы больше информации.

      PS Сам перешел на async/await так как код короче и симпатичнее.


  1. zapolnoch
    27.10.2017 12:38

    По первому пункту можно еще посмотреть на Napa.js от Microsoft. Multi-threaded JavaScript runtime.


  1. StarMarine
    27.10.2017 13:56

    Отличная статья!


  1. Juma
    28.10.2017 00:45

    Оффтоп.
    В последнее время очень часто встречаю статьи и комментарии, в которых многие очень хвалят VS Code. Вот и Азат Мардан тоже хвалит. Да действительно, по сравнению с другими подобными редакторами он очень удобен. Куча полезных функций и дополнений. Но я не могу на него перейти. Мне удобно читать светлый текст на тёмном фоне (подсветка Monokai или похожая). Но в VS Code все светлые буквы кажутся (становятся) полужирными на тёмном фоне. Такой эффект проявляется во всех редакторах на основе Electron, а так же в хроме. По крайней мере на моей машине (Win 10, монитор 22" FullHD). И решения на данный момент мне найти не удалось.


    1. Tantrido
      28.10.2017 05:25

      Сижу в IntelliJ IDEA — по моему пока лучшая среда для JS и node.js.


  1. Tantrido
    28.10.2017 05:24

    Порой вам нужно запустить код, который потребляет исключительно вычислительную мощность процессора. Поскольку в этот момент не производится операций ввода-вывода, ваша Node.js-программа блокируется до завершения этого кода. Если речь идет об интенсивно потребляющей процессор операции (например, криптографии), это может дать отрицательный эффект, поскольку ваша Node-программа не сможет ничего сделать, пока операция не завершится
    Я не понял: а что нельзя этот код поместить в `async` функцию?!


    1. Druu
      28.10.2017 07:17

      Сам по себе async (без await) не делает ничего, то есть код в async ф-и без await'ов исполняется полностью синхронно, блокируя тред. Освобождение треда происходит именно внутри await.


      1. Tantrido
        28.10.2017 07:21

        Это ежу понятно. async/await в паре решат описанную «проблему» или нет?


        1. Druu
          28.10.2017 07:33

          Нет, с чего бы? Await положит вам микротаску в очередь, когда она начнет исполняться — заблокирует тред. Всякие библиотечные функции выполняются асинхронно не потому, что на них await, а потому, что они так сами по себе реализованы, внутри.

          То есть, вам надо раздробить ф-ю на несколько кусков и в конце выполнения каждого из кусков спавнить таску на выполнение следующего (например, через timeout(0)). Тогда поле каждого куска управление будет возвращаться.


          1. Tantrido
            28.10.2017 07:40

            Расстроили вы меня. Думал async/await всё решает. Т.е. для таких задач нужно искать какой-либо пакет типа волокон, который поместит долгую задачу в отдельный поток?! Т.е. async/await этого не делают?


            1. Druu
              28.10.2017 07:54

              > Т.е. async/await этого не делают?

              Да, в другой поток async/await сами по себе ничего не кладут, по крайней мере в js.


              1. Tantrido
                31.10.2017 11:39
                -1

                Да, в другой поток async/await сами по себе ничего не кладут, по крайней мере в js.
                Хм… в C# кладут, это и ввело в заблуждение. Пусть в JS не кладут, но ведь сказано, что async функция не блокирует главный поток. Т.е всё равно нужно чтобы где-то в глубине асинхронной функции создавался отдельный поток и возвращался промис (Everything runs on a different thread except our code)?

                Почему тогда пишут, что о промисах можно забыть? Т.е. мне всё равно где-то придётся создать промис для долгой операции (вычислений)? Если у меня например REST сервер выполняет долгую async функцию для одного пользователя, будет ли она блокировать запросы другого пользователя?! Или здесь можно использовать npm модуль async?

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


                1. mayorovp
                  31.10.2017 13:37

                  И в C# тоже не кладут.


                  1. Tantrido
                    31.10.2017 13:50

                    Тогда вообще непонятно как и зачем всё это работает. Как мне узнать когда async функция заблокирует главный поток, а когда — нет. Например, синхронный вызов к БД хочу сделать асинхронным (неблокирующим):

                    better-sqlite3 — синхронная библиотека:

                    var Database = require('better-sqlite3');
                    
                    var db = new Database('./my_db.sqlite');
                    
                    async function DBRequest() {
                        var row = db.prepare("SELECT * FROM table");
                        return row;
                    };


                    1. Druu
                      31.10.2017 15:12

                      > Как мне узнать когда async функция заблокирует главный поток, а когда — нет.

                      Async ф-я — это обычная ф-я, отличается она исключительно тем, что внутри нее можно делать await. Блокировать поток она будет всегда, когда его будет блокировать та же самая ф-я без async. Равно и обратное — если ф-я без async не будет блокировать поток, то не заблокирует и с async.


                      1. Tantrido
                        31.10.2017 15:48
                        -1

                        Глупость какая-то. В C# async функция возвращает Task, который запускает её асинхронно в пуле потоков. В JS async возвращает Promise… который никак к потокам не относится:

                        var promise = new Promise(function(resolve, reject) {
                          // Эта функция будет вызвана автоматически
                        
                          // В ней можно делать любые асинхронные операции,
                          // А когда они завершатся — нужно вызвать одно из:
                          // resolve(результат) при успешном выполнении
                          // reject(ошибка) при ошибке
                        })


                        А синхронные операции получается в промисе делать нельзя. Замкнутый круг получается. Т.е. node может делать асинхронные функции и создаёт для них отдельные потоки или процессы, а программист нет?!


                        1. mayorovp
                          31.10.2017 16:55

                          В C# async функция возвращает Task, который запускает её асинхронно в пуле потоков

                          Каким таким хитрым образом возвращаемое функцией значение может запустить ее?


                    1. faiwer
                      31.10.2017 15:35

                      Async-функции это просто синтаксический сахар над промисами. JS как был однопоточным без них, так таковым и остался с ними. Функция выполняется до ближайшего await как обычная. await должен стоять перед promise-ом. Собственно интерпретатор дойдя до await выйдет из async функции, и вернётся к ней только тогда, когда этот promise (после await) от-resolve-ится. И продолжит выполнение до следующего await-а. И так далее. Это просто "сахар", не более.


                      Это не волокна, не потоки, не процессы. babel трансформирует их в обычные функции, разрезав её на кусочки, и организовав эти кусочки в малопонятный конечный автомат (там switch-case, если мне не изменяет память).


                      В живую, когда браузер поддерживает их нативно, происходит примерно то же самое. В общем никакой магии. Просто сахар.


                      1. Tantrido
                        31.10.2017 15:50

                        1. faiwer
                          31.10.2017 16:40

                          Т.е. node может делать асинхронные функции и создаёт для них отдельные потоки или процессы

                          Вы похоже совсем запутались.


                          1. Async-functions не имеют ничего общего с потоками. И с процессами тоже. Это обычные функции. Обычные синхронные функции. Просто хитрые. Почитайте статьи. Никаких тредов. Всё тот же event-loop. Просто с сахарком.
                          2. А различного рода node-cluster и пр. похожие примитивы просто запускают node повторно для каждого fork-а. Общей памяти между этими процессами нет.

                          А синхронные операции получается в промисе делать нельзя

                          Не понял, что вы хотели этим сказать. Почему нельзя? Как это вообще возможно?


                          Или вы про то, что async-функция даже с синхронными операциями от-resolve-ится не в текущем тике? Ну это для удобства сделано, чтобы единообразно всё было. В противном случае будут разные плавающие трудно-вылавливаемые баги.


              1. Tantrido
                31.10.2017 13:04

                Understanding the node.js event loop

                Of course, on the backend, there are threads and processes for DB access and process execution.


            1. mayorovp
              29.10.2017 09:16

              Волокна тоже не помогут, потому что они делают то же самое — выполняют все в одном потоке.


              Такие задачи надо выносить в другие процессы. child_process.fork вам в помощь


              1. Tantrido
                29.10.2017 09:28

                Понятно, спасибо. Ну или nodejs.org/api/cluster.html А какой-либо библиотеки, которая создаёт потоки, а не процессы нет?



                1. babylon
                  29.10.2017 20:39

                  По классике контекст потока определяется контекстом процесса, а не наоборот