В качестве аперитива попробуем угадать что выведет в консоль следующий код:
- loop-promise.js:
'use strict'; const interval = setInterval(() => { console.log('\nsetInterval'); }, 0); setTimeout(() => { console.log('setTimeout 1'); Promise.resolve() .then(() => { console.log('promise 3'); }).then(() => { console.log('promise 4'); }).then(() => { setTimeout(() => { console.log('setTimeout 2'); Promise.resolve() .then(() => { console.log('promise 5'); }).then(() => { console.log('promise 6'); }).then(() => { clearInterval(interval); }); }, 0); }); }, 0); Promise.resolve() .then(() => { console.log('promise 1'); }).then(() => { console.log('promise 2'); });
- loop-next-tick.js:
'use strict'; const interval = setInterval(() => { console.log('\nsetInterval'); }, 0); setTimeout(() => { console.log('setTimeout 1'); process.nextTick(() => { console.log('nextTick 3'); process.nextTick(() => { console.log('nextTick 4'); setTimeout(() => { console.log('setTimeout 2'); process.nextTick(() => { console.log('nextTick 5'); process.nextTick(() => { console.log('nextTick 6'); clearInterval(interval); }); }); }, 0); }); }); }); process.nextTick(() => { console.log('nextTick 1'); process.nextTick(() => { console.log('nextTick 2'); }); });
Несколько секунд напрягаем извилины...
... я не угадал.
Переходим к основному блюду. Создадим C/C++ аддон. node-gyp у меня уже установлен. C/C++ не моя тема, поэтому установим Native Abstractions for Node.js:
Пишем код аддона:
- addon.cc:
#include <nan.h> // includes v8 using namespace Nan; using namespace v8; class IncWorker : public AsyncWorker { public: IncWorker(Callback *callback, int i) : AsyncWorker(callback), i(i) {} ~IncWorker() {} void Execute () { ++i; } void HandleOKCallback () { Nan::HandleScope scope; Local<Value> argv[] = { Null(), New<Number>(i) }; callback->Call(2, argv); } private: int i; }; NAN_METHOD(Inc) { int i = To<int>(info[0]).FromJust(); Callback *callback = new Callback(info[1].As<Function>()); AsyncQueueWorker(new IncWorker(callback, i)); } NAN_METHOD(IncSync) { int i = To<int>(info[0]).FromJust(); info.GetReturnValue().Set(++i); } NAN_MODULE_INIT(Init) { Nan::Set(target, New<String>("inc").ToLocalChecked(), GetFunction(New<FunctionTemplate>(Inc)).ToLocalChecked()); Nan::Set(target, New<String>("incSync").ToLocalChecked(), GetFunction(New<FunctionTemplate>(IncSync)).ToLocalChecked()); } NODE_MODULE(addon, Init)
Создаем файл конфигурации аддона:
- binding.gyp:
{ "targets": [ { "target_name": "addon", "sources": [ "addon.cc" ], "include_dirs" : ["<!(node -e \"require('nan')\")"], } ] }
Генерируем файлы билда:
Билдим:
Тестируем:
- addon.js:
'use strict'; const addon = require('./build/Release/addon'); addon.inc(21, (err, result) => { console.log('inc:', result); }); console.log('incSync:', addon.incSync(11));
Для наглядности добавим в асинхронный код задержку выполнения 100 миллисекунд (Sleep(100)):
- addon.cc:
//----- class IncWorker void Execute () { Sleep(100); ++i; } //-----
Билдим заново:
Тестируем...
- addon.js:
'use strict'; const addon = require('./build/Release/addon'); const log = (i, arr) => console.log(`${i}\t${(arr[0] * 1e3 + arr[1] * 1e-6).toFixed(3)} ms`); const fn = (i, arr) => { return new Promise((resolve, reject) => { addon.inc(i, (err, result) => { if (err) return reject(err); log(result, process.hrtime(arr)); resolve(); }); }); }; const i = 16; const arr = process.hrtime(); Promise.all([...new Array(i)].map((_, i) => fn(i, arr))) .then(() => { process.exit(0); }) .catch((err) => { console.error(err); process.exit(1); });
Создаем переменную окружения по имени UV_THREADPOOL_SIZE:
Тестируем...
... spooky :)
Ссылки:
- Node's Event Loop From the Inside Out by Sam Roberts, IBM
- Everything You Need to Know About Node.js Event Loop - Bert Belder, IBM
- On problems with threads in node.js
- Developer initiates I/O operation. You won't believe what happens next
- Understanding the Node.js Event Loop - Node.js at Scale
- Building an Asynchronous C++ Addon for Node.js using Nan
Комментариев нет:
Отправить комментарий
Комментарий будет опубликован после модерации