Napi Thread-Safe Callback
This package contains a header-only C++ helper class to facilitate calling back into JavaScript from threads other than the Node.JS main thread.
Examples
Perform async work in new thread and call back with result/error
void example_async_work(const CallbackInfo& info){ // Capture callback in main thread auto callback = std::make_shared<ThreadSafeCallback>(info[0].As<Function>()); bool fail = info.Length() > 1; // Pass callback to other thread std::thread([callback, fail] { try { // Do some work to get a result if (fail) throw std::runtime_error("Failure during async work"); std::string result = "foo"; // Call back with result callback->call([result](Napi::Env env, std::vector<napi_value>& args) { // This will run in main thread and needs to construct the // arguments for the call args = { env.Undefined(), Napi::String::New(env, result) }; }); } catch (std::exception& e) { // Call back with error callback->callError(e.what()); } }).detach();}
Perform async work in new thread, call back with result/error and check return value
void example_async_return_value(const CallbackInfo& info){ auto callback = std::make_shared<ThreadSafeCallback>(info[0].As<Function>()); std::thread([callback] { try { int result = 0; while (true) { // Do some work... try { result += 1; if (result > 20) throw std::runtime_error("Failure during async work"); } catch (std::exception &e) { // Call back with error // Note that this will cause the thread to exit and the // ThreadSafeCallback to be destroyed. But the callback // will still be called, even afterwards callback->callError(e.what()); throw; } // Call back into Javascript with current result auto future = callback->call<bool>( [result](Env env, std::vector<napi_value> &args) { args = {env.Undefined(), Number::New(env, result)}; }, [](const Value &val) { return val.As<Boolean>().Value(); }); // This blocks until the JS call has returned a value // In case the callback throws we just let the exception // bubble up and end the thread auto continue_running = future.get(); if (!continue_running) break; } } catch (...) { // Ignore errors } }).detach();}
Usage
- Add a dependency on this package to
package.json
:
"dependencies":
- Reference this package's include directory in
binding.gyp
:
'include_dirs': ["<!@(node -p \"require('napi-thread-safe-callback').include\")"],
- Include the header in your code:
#include "napi-thread-safe-callback.hpp"
Exception handling
Exceptions need to be enabled in the native module. To do this use the following
in binding.gyp
:
'cflags!': [ '-fno-exceptions' ], 'cflags_cc!': [ '-fno-exceptions' ], 'xcode_settings': { 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', 'CLANG_CXX_LIBRARY': 'libc++', 'MACOSX_DEPLOYMENT_TARGET': '10.7', }, 'msvs_settings': { 'VCCLCompilerTool': { 'ExceptionHandling': 1 }, }, 'conditions': [ ['OS=="win"', { 'defines': [ '_HAS_EXCEPTIONS=1' ] }] ]