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

Он длинный и уродливый
Может перепишите этот код так, что бы он был короче и более читаемым?
 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 })

    }

})

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

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

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

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

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

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

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

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

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

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

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

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

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.

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

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

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

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

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


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


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()
        }
    }
}
И как теперь, например, сделать два find-запроса параллельно?
const db = {
    users : {
        find : params => {
            const future = new Future
            setTimeout( ()=> {
                future.return([ { id : 1 , name : 'Jin' } ])
            } , 1000 )
            return new Proxy( [] , { get : ()=> future.wait() } )
        }
    }
}
Вы не поняли. Я говорю о том, что у вас есть одна find, и я хочу сперва сделать пару запросов последовательно, а потом — параллельно.

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


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

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

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.5 года никто не перепроверил? Там уж несколько версий ядер сменилось и самого арча, автору пофиг?

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

В одном единственном тестовом файле. И только для того, что бы:
to simulate a Meteor environment
А можете более детально обосновать, почему он длинный и уродливый? Хотелось бы больше информации.

PS Сам перешел на async/await так как код короче и симпатичнее.
Оффтоп.
В последнее время очень часто встречаю статьи и комментарии, в которых многие очень хвалят VS Code. Вот и Азат Мардан тоже хвалит. Да действительно, по сравнению с другими подобными редакторами он очень удобен. Куча полезных функций и дополнений. Но я не могу на него перейти. Мне удобно читать светлый текст на тёмном фоне (подсветка Monokai или похожая). Но в VS Code все светлые буквы кажутся (становятся) полужирными на тёмном фоне. Такой эффект проявляется во всех редакторах на основе Electron, а так же в хроме. По крайней мере на моей машине (Win 10, монитор 22" FullHD). И решения на данный момент мне найти не удалось.
Порой вам нужно запустить код, который потребляет исключительно вычислительную мощность процессора. Поскольку в этот момент не производится операций ввода-вывода, ваша Node.js-программа блокируется до завершения этого кода. Если речь идет об интенсивно потребляющей процессор операции (например, криптографии), это может дать отрицательный эффект, поскольку ваша Node-программа не сможет ничего сделать, пока операция не завершится
Я не понял: а что нельзя этот код поместить в `async` функцию?!
Сам по себе async (без await) не делает ничего, то есть код в async ф-и без await'ов исполняется полностью синхронно, блокируя тред. Освобождение треда происходит именно внутри await.
Это ежу понятно. async/await в паре решат описанную «проблему» или нет?
Нет, с чего бы? Await положит вам микротаску в очередь, когда она начнет исполняться — заблокирует тред. Всякие библиотечные функции выполняются асинхронно не потому, что на них await, а потому, что они так сами по себе реализованы, внутри.

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

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

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

Кучу уже статей прочёл, а до конца не понятно. Не хочется налететь на грабли при высокой нагрузке на сервер.
Тогда вообще непонятно как и зачем всё это работает. Как мне узнать когда 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;
};
> Как мне узнать когда async функция заблокирует главный поток, а когда — нет.

Async ф-я — это обычная ф-я, отличается она исключительно тем, что внутри нее можно делать await. Блокировать поток она будет всегда, когда его будет блокировать та же самая ф-я без async. Равно и обратное — если ф-я без async не будет блокировать поток, то не заблокирует и с async.
Глупость какая-то. В C# async функция возвращает Task, который запускает её асинхронно в пуле потоков. В JS async возвращает Promise… который никак к потокам не относится:

var promise = new Promise(function(resolve, reject) {
  // Эта функция будет вызвана автоматически

  // В ней можно делать любые асинхронные операции,
  // А когда они завершатся — нужно вызвать одно из:
  // resolve(результат) при успешном выполнении
  // reject(ошибка) при ошибке
})


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

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

Каким таким хитрым образом возвращаемое функцией значение может запустить ее?
Я понял, т.е. чтобы в C# функция реально была асинхронной (неблокирующей) мне нужно в ней создать Task, запустить его Task.Run() и вернуть из функции, а в Task.Run(<метод>) поместить метод, который и будет выполнять асинхронный код. Верно?

Можно сделать аналогично в JS?
В JS и C# это работает совсем по разному. В C# Task будет выполняться в отдельном потоке из пула потоков и если там например какие-то тяжелые вычисления, основной поток блокироваться не будет.
JS — однопоточный, весь ваш код будет выполняться в одном потоке. И даже если обернуть код в Promise, он будет выполняться в главном потоке и тяжелые вычисления будут блокировать его. При этом он будет вызван асинхронно, да.
function factorialize(num) {
  return new Promise((resolve) => {
    let result = num;
    if (num === 0 || num === 1) 
      return 1; 
    while (num > 1) { 
      num--;
      result *= num;
    }
    resolve(result);
  });
}
factorialize(1000000000).then(console.log); // асинхронный вызов
> Глупость какая-то. В C# async функция возвращает Task, который запускает её асинхронно в пуле потоков.

Нет, не запускает, читайте спеку внимательнее.

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

Кажется, именно это и написано по вашей же ссылке ниже:

> Node.js keeps a single thread for your code…
> It really is a single thread running: you can't do any parallel code execution;
Всё что я понял: сам Node.js может сделать вызов блокирующей функции неблокирующим поместив её в отдельный поток:

stackoverflow.com/a/3657155/630169
Node.js wraps the blocking system call in a thread.

nodesource.com/blog/understanding-the-nodejs-event-loop
You may have heard that Node has a thread pool, and might be wondering «if Node pushes all those responsibilities down why would a thread pool be needed?» It's because the kernel doesn't support doing everything asynchronously. In those cases Node has to lock a thread for the duration of the operation so it can continue executing the event loop without blocking.

А программисту такой возможности не предоставляет автоматом. Разве что использовать сторонний npm пакет типа github.com/Microsoft/napajs

Ну если я всё правильно понял, то тут речь о том, что системные вызовы не всегда умеют в асинхронноть, а вызывать их как-то нужно, не блокируя основной тред. Стало быть они запускаются в отдельном треде. Всё это внутренности nodejs, и по большому счёту, к нам отношения не имеют. Вы ещё в недры парсера и JIT в V8 загляните в поисках потоков :) Соглашусь с:


We can enjoy Node.js because it hides the ugly and cumbersome details behind an event-driven asynchronous architecture

Касательно:


Разве что использовать сторонний npm пакет типа…

А вам зачем? Мне кажется, что если речь всерьёз заходит про использование тредов в рамках nodejs приложения, то вы свернули куда-то не туда и выбрали, возможно, вообще не тот инструмент. Насколько я могу судить по примеру в README, napajs не даёт вам тех преимуществ многопоточной разработки, которую вы имеете, скажем в c++. И применимость таких штук в реальном приложении, как минимум под вопросом.

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

Ну тут я вижу 2 варианта, когда такая блокировка возможна:


  1. Обычный JS-код в каком-нибудь долгом цикле. Все длительные вычисления принято писать так, чтобы они были разбиты мелкие части и выполнялись асинхронно. Однако если так не сделать, то весь тред будет ждать завершения этой задачи.
  2. Использование каких-либо блокирующих системных вызовов. Обычно всякие сторонние пакеты, которые запускают внешний код (какую-нибудь с++ программу или системные вызовы) имеют как синхронный, так и асинхронный варианты. Если выбрать первый — да, всё повиснет в ожидании.
  3. Использовать асинхронный вариант из п2, при условии, что он написан ногами. Я с таким не сталкивался.
В общем простого варианта как в C# нет, почему тогда во всех статьях async/await в Node.js позиционируется как аналог async/await в C#?! Это и ввело меня в заблуждение — а это разные механизмы всё таки: в C# я могу в тред запихнуть вычисления и сделать любую функцию асинхронной, а в node — нет — это только обёртка над асинхронными операциями, которые сделаны таковыми иными механизмами.

Я право не знаю как оно там в C# устроено. Но в JS это просто синтаксический сахар. Может быть кто-то их позиционирует тем же образом именно из-за схожести синтаксиса.

async/await в Node.js является полным аналогом async/await из C#

Класс Promise в Node.js не является полным аналогом Task из C#

Статический метод Task.Run не имеет никакого отношения к async/await

Так понятнее?
async/await в Node.js является полным аналогом async/await из C#
А статьи врут, что является, причём говорят, что о промисах можно забыть, что и ввело в заблуждение:

  1. habrahabr.ru/company/ruvds/blog/326074
  2. blog.risingstack.com/mastering-async-await-in-nodejs
  3. и др.
Класс Promise в Node.js не является полным аналогом Task из C#
Это я тоже понял. Непонятно, почему Node.js отказывает программисту в механизме, который использует сам? Или может где-то в npm есть подобный механизм?
Тогда напиши на Node.js async/await функцию, которая будет выполняться в отдельном треде как в C#
Тогда напиши на Node.js async/await функцию, которая будет выполняться в отдельном треде как в C#

Ну это реально. Подключите эту вашу napajs, сделайте для неё promise обёртку, и используйте с async-await. Можете даже написать абстракцию похожую на Task из C#.


Отдельный вопрос, конечно, на кой чёрт вам всё это потребовалось в nodejs, где приняты совсем другие соглашения и подходы, но если сильно хочется и колется — вперёд на амбразуры. Если napajs будет валить ваше приложение в segmentation fault — я не виноват )


И да, не ожидайте, что в функции, которую вы организуете в napajs у вас будут все нужные вам возможности. Скорее вы будете как в смерительной рубашке.

> Тогда напиши на Node.js async/await функцию, которая будет выполняться в отдельном треде как в C#

За выполнение в отдельном треде в шарпе отвечает Task.Run. Task.Run создает таску в отдельном треде. По дефолту Task и await/async в шарпе ничего общего с выделением тредов не имеют (как и в js), все исполняется в одном треде.
Да, я понял уже: асинхронно выполнится только та функция, которая уже асинхронна, произвольная — нет. В C# надо использовать Task.Run(), в node — что-то вроде npm threads.
В C# надо использовать Task.Run(), в node — что-то вроде npm threads.

Нет-нет. Не так. В node надо просто писать node way, а не городить какую-то наркоманию. Ей богу. Если потоки для вас настолько принципиальны, то лучше возьмите Java, C# и пр. И быстрее и надёжнее будет, чем брать чей-то непонятный костыль, который выглядит как смерительная рубажка, и чёрт знает на каких щах работает.


Ваши сообщения мне напоминают ту бородатую шутку:


Программист на Фортране, пишет на Фортране на любом языке
Я вот тоже задумываюсь, чтобы на следующем проекте вернуться на C# ;)
А статьи врут, что является, причём говорят, что о промисах можно забыть, что и ввело в заблуждение

"не читайте до обеда советских газет". Async\Await это обёртка именно над promise-ми. Более того в реальном коде используется с ними в перемешку. await работает только в async контексте, но работать с async функцией можно и через простой .then.


Непонятно, почему Node.js отказывает программисту в механизме, который использует сам?

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


Однопоточный event-loop подход позволяет сильно упростить сложные вещи. К примеру не нужны shared-memory, не нужны synchronize-примитивы, семафоры и пр. сложные штуки для одновременной работы разных потоков над одними и теми же данными.

Т.е. мне нужно переписать приложение и запустить столько процессов, сколько у меня в системе процессоров (ядер)?

 for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

И в принципе это проблему частично решит?

Высоко-нагруженные node приложения обычно так и пишут. Запускают столько fork-ов, сколько ядер в системе. Но чаще запускают через какой-нибудь pm2, а не руками. Он умеет поднимать упавшие запросы, балансировать нагрузку, мониторинг и пр.


А в dev режиме проще работать с 1-им процессом.
В общем если изначально не рассчитывать на in-memory кеш в рамках самого node-процесса (т.е. не хранить в переменных те данные, которые могут устареть ввиду того, что другой процесс как-то повлияет на их источник), то возможно ничего переписывать и не потребуется.


Опять же, в зависимости от приложения, может хватить и 1-го процесса. Скажем если 95% нагрузки падает на СУБД (она и без того многопоточна).

А вот, например, корутины могут проблему решить? Пишут, что каждая корутина выполняется в отдельном треде. Если с использованием Bluebird или Q написать — можно ли с их помощью любую функцию сделать неблокирующей?

Автор пишет, что


Couroutines [5] are a single-threaded version of multi-tasking: Each coroutine is a thread, but all coroutines run in a single thread and they explicitly relinquish control via yield.

Но вся статья почему-то о генераторах (те же async-await но для итерации, а не для асинхронноти), а не о корутинах. Чуть ниже автор правда пишет:


Generators are shallow co-routines

Видимо словом "shallow" он хотел сказать, что они только похожи на корутины. Но не сделал это явным образом.


В общем нет, в JS нет никаких корутин. И статья по вашей ссылке тоже их не содержит. А устройство async-await и генераторов во многом одинаковое. Более того, async-await легко транспайлится в эти самые генераторы.


Tantrido вы определённо копаете не в ту сторону. Хотите узнать как писать высоконагруженные штуки на node? Почитайте про event-loop, cluster, pm2, и пр. node-specific техн. статьи.


Эта ваша статья про генераторы одна из сотен. Они были написаны до того, как появились async-await, но уже появились генераторы. И народ массово использовал генераторы в том же ключе, что сейчас использует async-await. Там даже автозаменой по коду пройтись можно.

Почитайте про event-loop, cluster, pm2, и пр. node-specific
Причитал и, в частности, про cluster, pm2. Они мне не очень понравились (может непривычно :), т.к. программа ограничивается всего несколькими процессами (в C# можно создавать сколько угодно потоков) и непонятно хватит ли их в реальном приложении. node-specific тоже начал читать:
Посмотрим как такие вещи разруливаются.

А вообще заметил ещё при переходе с C++ на C#, что многие вещи призванные упростить разработку на самом деле её усложняют. В C# — это была сборка мусора, в Node.js — однопоточность вместо много поточности т.п.

Сборка мусора усложнила разработку? Серьёзно? Выделять память руками, писать деструкторы, тщательно следить за границами, разные сложности с указателями и прочие очевидно непростые штуки вам показались проще, чем их отсутствие? Или речь идёт о том, что те методы хирургии, которые были у вас в арсенале на C++, и пропали в C#, были вам столь дороги, что их отсутствие обернулось большим дискомфортом? ) Да ладно?

Да, когда сам отвечаешь за утечки и знаешь, где они могут возникнуть — проще. Дискомфорт был несколько раз с C# когда размер программы начинал увеличиваться до огромных размеров, т.е. где-то не освобождались ресурсы: переменная продолжала ссылаться на объект в памяти или не высвобождались unmanaged ресурсы, приходилось использовать using() {...}, чтобы объект сразу уничтожался после использования. Нужно чётко знать как работает сборщик мусора иначе утечки неизбежны. Так где-же простота?! В C++ следишь только за местом, где выделяешь ресурсы, а здесь нужно следить ещё, что кто-то ссылается на объект и т.п. Т.е. я просто выкинул деструкторы для «простоты», а стиль написания программ не поменял — это привело к различным утечкам в памяти в разных проектах… и сложностях на собеседовании :) В Java всё-таки попроще, не заморочек со сборкой мусора.
> Так где-же простота?! В C++ следишь только за местом, где выделяешь ресурсы, а здесь нужно следить ещё, что кто-то ссылается на объект и т.п.

А что произойдет в с++, если вы освободите память из-под объекта, на который остались ссылки? ;)
программа ограничивается всего несколькими процессами… и непонятно хватит ли их в реальном приложении

Не понял, что вы этим хотели сказать. Вы можете запускать новые процессы до тех пор, пока ОС вам горло не перекроет. И что значит хватит? :) Ну или что значит не-хватит? Скажем покажите на примере C# приложения.


может непривычно

Ну по понятным причинам это положение вещей не всем нравится. Однако это так. И любые подкопы в сторону (те же node-fibers) это уже заведомый риск. А вы вроде ведёте речь о больших проектах, где такие риски недопустимы. Кстати, какую, если не секрет, нагрузку вы ожидаете покрыть?

Потенциально могут быть тысячи пользователей одновременно. Но у меня ещё BotBuilder поверх Restify используется — тоже узкое горлышко: ответы с заметной задержкой приходят даже для одного пользователя — несколько секунд.

nginx с такой же примерно архитектурой умеет обрабатывать запросы тысячи пользователей одновременно. Всё в ваших руках.

Видимо словом "shallow" он хотел сказать, что они только похожи на корутины. Но не сделал это явным образом.

Нет, слово shallow обозначало что фичи сопрограммы распространяются только на 1 фрейм стека.

Всё таки не зря интересовался — подобными вопросами люди задавались и до меня:
But what's with longish, CPU-bound tasks?
How do you avoid blocking the event loop, when the task at hand isn't I/O bound, and lasts more than a few fractions of a millisecond? You simply can't, because there's no way… well, there wasn't before threads_a_gogo.
и для их решения создали уже кучу модулей, а некоторые, такие как threads, работают и в браузере и в Node.js:


Ещё бы кто async к ним прикрутил — было бы прекрасно. Кстати вот товарищ тесты погонял на Threads à gogo — результаты с тредами в 40х быстрее, чем с Cluster. Так что идея Node.js не всегда хорошо работает.

И чем этот threads gogo принципиально отличается от вышеописанного napajs? Вы передаёте ему сериализуемый метод (для eval) и некую команду (для eval), которые он eval-ит в новосозданном треде. Скажите, какое отношение это… имеет к нормальному программированию? :)


Нет, ну можно, конечно, вынести какой-то жутко нагруженный pure-method в пул потоков. Но как-то это в целом слабо вяжется со всем тем, что на node делают на самом деле. Это уже всё какая то специфика. Причём на грани фола.

Но как-то это в целом слабо вяжется со всем тем, что на node делают на самом деле.
И что же они делают на самом деле? Я просто его только изучаю, хотя пара готовых проектов уже есть. Может привычки ещё от других языков. Но ведь всегда может найтись функция, которая излишне нагрузит главный поток, что её лучше будет вынести в отдельный тред, тем более, что тесты показывают, что такой подход может быть более производителен.
И что же они делают на самом деле?

Типичное node приложение это сотни (тысячи, миллионы, ...) js-файлов, содержащих разный контекст, множество методов, использующих this, scope и пр., классы и пр. В общем всё то, что вы не сможете сериализовать в строчку и за-eval-ить в новосозданном треде. Такая же судьба и у всех данных, которыми вы пожелаете общаться между тредами — далеко не любой объект сериализуется.


Но ведь всегда может найтись функция

Node-way в этом случае — разбить эту функцию на мелкие части, чтобы нагружать перестала. Хотя я даже с такой ситуацией, пока не сталкивался.


что тесты показывают, что такой подход может быть более производителен

Честно говоря, если для вас ребром стоит вопрос производительности, то:


  • либо вы выбрали не тот инструмент (на секундочку js это язык интерпретируемый язык со слабой дин. типизацией, он вообще не про скорость)
  • можно вынести этот метод в c++/rust библиотеку и разгрузить не только тред, а вообще ускорить этот участок в разы.
Node-way в этом случае — разбить эту функцию на мелкие части, чтобы нагружать перестала.
Я привёл выше синхронную функцию, например, которая состоит ВСЕГО из одного синхронного оператора, который в моём случае по счастью выполняется быстро, но всё может поменяться в будущем. Такую функцию разбить на мелкие части не получится.

либо вы выбрали не тот инструмент
Инструмент я выбрал самый тот!!! :) Очень быстрая и удобная разработка, очень приятная в сравнении с тем же C#. И потом BotBuilder под Линукс существует только в варианте для node, для .Net Core 2 ещё не портировали до конца ;)

(на секундочку js это язык интерпретируемый язык со слабой дин. типизацией, он вообще не про скорость)
Не знаю, все верещат, что он ОЧЕНЬ быстрый. Вот сегодня тоже запускал тесты оказалось, что в 1.5 — 3 раза быстрее дотнета.
> Всё что я понял: сам Node.js может сделать вызов блокирующей функции неблокирующим поместив её в отдельный поток:

Не совсем так. ИО-функции в ноде _сами по себе_ неблокирующие, точно так же как неблокирующим является Task.Run из шарпа. Представьте, что вы написали на шарпе ф-ю, которая делает Task.Run, в котором выполняет какие-то ИО-операции, а потом вы эту ф-ю (создающую Task и возвращающую его) вызываете из ноды. Не важно, как вы ее вызвали — главное, что сама ф-я, просто по способу своей реализации, асинхронна.

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


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


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

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

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


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

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

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


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

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


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

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

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.