Created
June 19, 2018 00:54
-
-
Save chad3814/50d75d4f13054dc47de8c897f303580e to your computer and use it in GitHub Desktop.
async worker/emitter example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"targets": [ | |
{ | |
"target_name": "AsyncEmitter", | |
"sources": [ "emitter.cc" ], | |
"include_dirs": [], | |
"dependencies": [], | |
"cflags!": [ "-fno-exceptions" ], | |
"cflags_cc!": [ "-fno-exceptions" ], | |
"libraries": [], | |
"xcode_settings": { | |
"GCC_ENABLE_CPP_EXCEPTIONS": "YES", | |
"CLANG_CXX_LIBRARY": "libc++", | |
"MACOSX_DEPLOYMENT_TARGET": "10.7", | |
}, | |
"msvs_settings": { | |
"VCCLCompilerTool": { "ExceptionHandling": 1 }, | |
}, | |
}, | |
], | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <node.h> | |
#include <uv.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
namespace asyncAddon { | |
using v8::Persistent; | |
using v8::Function; | |
using v8::Isolate; | |
using v8::Local; | |
using v8::Value; | |
using v8::Number; | |
using v8::FunctionCallbackInfo; | |
using v8::Object; | |
/** | |
* Work structure is be used to pass the callback function and data | |
* from the initiating function to the function which triggers the callback. | |
*/ | |
struct Work { | |
uv_work_t request; | |
uv_async_t async; | |
Persistent<Function> callback; | |
int seconds; | |
}; | |
static void emit(uv_async_t *async) { | |
Isolate * isolate = Isolate::GetCurrent(); | |
v8::HandleScope handleScope(isolate); | |
Work *work = static_cast<Work *>(async->data); | |
Local<Value> argv[1] = {Number::New(isolate, work->seconds)}; | |
Local<Function> callback = Local<Function>::New(isolate, work->callback); | |
callback->Call(isolate->GetCurrentContext()->Global(), 1, argv); | |
} | |
/** | |
* WorkAsync function is the "middle" function which does the work. | |
* After the WorkAsync function is called, the WorkAsyncComplete function | |
* is called. | |
*/ | |
static void WorkAsync(uv_work_t *req) { | |
Work *work = static_cast<Work *>(req->data); | |
for (int i = 0; i < 5; i++) { | |
work->seconds = (rand() %5) + 1; | |
sleep(work->seconds); | |
// this signals the emit func, but on the main thread | |
uv_async_send(&work->async); | |
} | |
} | |
/** | |
* WorkAsyncComplete function is called once we are ready to trigger the callback | |
* function in JS. | |
*/ | |
static void WorkAsyncComplete(uv_work_t *req, int) | |
{ | |
Isolate * isolate = Isolate::GetCurrent(); | |
v8::HandleScope handleScope(isolate); | |
Work *work = static_cast<Work *>(req->data); | |
//printf("status %d\n",status); | |
Local<Value> *argv = NULL; | |
Local<Function> callback = Local<Function>::New(isolate, work->callback); | |
callback->Call(isolate->GetCurrentContext()->Global(), 0, argv); | |
work->callback.Reset(); | |
uv_close((uv_handle_t *)&work->async, NULL); | |
delete work; | |
} | |
/** | |
* DoTaskAsync is the initial function called from JS. This function returns | |
* immediately, however starts a uv task which later calls the callback function | |
*/ | |
void DoTaskAsync(const FunctionCallbackInfo<Value>& args) { | |
Isolate* isolate = args.GetIsolate(); | |
Work * work = new Work(); | |
work->request.data = work; | |
work->async.data = work; | |
// args[0] is where we pick the callback function out of the JS function params. | |
// Because we chose args[0], we must supply the callback fn as the first parameter | |
Local<Function> callback = Local<Function>::Cast(args[0]); | |
work->callback.Reset(isolate, callback); | |
uv_async_init(uv_default_loop(), &work->async, emit); | |
uv_queue_work(uv_default_loop(), &work->request, WorkAsync, WorkAsyncComplete); | |
args.GetReturnValue().Set(Undefined(isolate)); | |
} | |
/** | |
* init function declares what we will make visible to node | |
*/ | |
void init(Local<Object> exports) { | |
NODE_SET_METHOD(exports, "runWorker", DoTaskAsync); | |
} | |
NODE_MODULE(NODE_GYP_MODULE_NAME, init) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
const {runWorker} = require('bindings')('AsyncEmitter'); | |
let interval; | |
const emit = function (seconds) { | |
if (seconds === undefined) { | |
console.log('async is done'); | |
clearInterval(interval); | |
return; | |
} | |
console.log('async slept for', seconds, 'seconds'); | |
}; | |
runWorker(emit); | |
interval = setInterval(() => console.log('second'), 1000); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "async-emitter", | |
"version": "1.0.0", | |
"license": "MIT", | |
"dependencies": { | |
"bindings": "^1.3.0" | |
}, | |
"scripts": { | |
"build": "node-gyp configure build --silent", | |
"clean": "node-gyp clean --silent" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment