The answer is that you typically want to use the background threads managed by Nodejs by submitting your work to the uv event queue, and then let nodejs worry about how to create and manage threads.
below is the boilerplate example which is missing from the node.js v0.10 manual.
struct Baton
{
// we need this structure to interact with the uv
// the uv_work_t must be the initial element and should store
// the callback function to be useful, but the rest
// is user defined depending on what is needed to actually do the work.
uv_work_t request;
v8::Persistent<v8::Function> callback;
// Add more elements to the structure as needed
int countdown;
};
static void AsyncTestWork (uv_work_t* req);
static void AsyncTestAfter(uv_work_t* req,int status);
static Handle<Value> AsyncTestPrep(const Arguments& args)
{
HandleScope scope;
if (args.Length() != 1) {
ThrowException(Exception::TypeError(String::New("Wrong number of arguments -- needs (callback)")));
return scope.Close(Undefined());
}
if (!args[0]->IsFunction()) {
ThrowException(Exception::TypeError(String::New("Wrong type of arguments -- needs (callback)")));
return scope.Close(Undefined());
}
v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(args[0]);
Baton* baton = new Baton();
baton->request.data = baton;
baton->callback = v8::Persistent<v8::Function>::New(callback);
baton->countdown = 3;
uv_queue_work(uv_default_loop(), &baton->request, AsyncTestWork, AsyncTestAfter);
return scope.Close(v8::Undefined());
}
static void AsyncTestWork (uv_work_t* req)
{
// This method will run in a seperate thread where you can do
// your blocking background work.
// In this function, you cannot under any circumstances access any V8/node js
// valiables -- all data and memory needed, MUSt be in the Baton structure
Baton* baton = static_cast<Baton*>(req->data);
sleep(6); // some fictional work, delaying the return....
baton->countdown -= 1; // my actual work in this
}
static void AsyncTestAfter(uv_work_t* req,int status)
{
// This is what is called after the 'Work' is done, you can now move any data from
// Baton to the V8/Nodejs space and invoke call back functions
Baton* baton = static_cast<Baton*>(req->data);
v8::Handle<v8::Value> argv1[] = { v8::Null(), Number::New(baton->countdown) };
v8::Handle<v8::Value> argv2[] = { v8::Null(), Number::New(23) };
v8::TryCatch try_catch;
// Call back to the JS function, you can make as many/few callbacks
// as you need, they just go on the event loop queue for now.
// Note: that it is mostly customary to call the callback
// function just (exactly) which is probably what you want
// to do to avoid confusing your users if you make a public api
baton->callback->Call(v8::Context::GetCurrent()->Global(), 2, argv1);
baton->callback->Call(v8::Context::GetCurrent()->Global(), 2, argv2);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
if (baton->countdown > 0) {
// resubmit the worker for yet more work
uv_queue_work(uv_default_loop(), &baton->request, AsyncTestWork, AsyncTestAfter);
} else {
// we are finished, clean up and be done
baton->callback.Dispose();
delete baton;
}
}
void init(Handle<Object> exports)
{
exports->Set(String::NewSymbol("myAsyncTestFunction"),
FunctionTemplate::New(AsyncTestPrep)->GetFunction());
}