Search code examples
javascriptnode.jsv8

How to promisify a node.js addon method


I'd like prepare a pull request to the node-java NPM package to support Promises/A+. This package exploits JNI to make it possible to uses Java classes from Javascript in node.js applications. For each imported java class, it exposes the methods of the class in both sync and async variants. The async variants use standard node.js callback functions which are easy to promisify from javascript (I have done so using both Q and Bluebird). But I'd like to enhance node-java so that it can automatically provide promisified methods. This is probably a trivial task for someone already experienced with v8 and node.js addons, but I am a total newbie with both. I'm hoping that someone here can save me what is probably at least a couple days of learning curve.

The critical piece of code I need to modify is in src/javaObject.cpp and looks like this (I have added some comments not in the source on github):

for(std::list<jobject>::iterator it = methods.begin(); it != methods.end(); ++it) {
  jstring methodNameJava = (jstring)env->CallObjectMethod(*it, method_getName);
  assert(!env->ExceptionCheck());
  std::string methodNameStr = javaToString(env, methodNameJava);

  // Create the async method variant, named exactly as the java method is named
  v8::Handle<v8::String> methodName = NanNew<v8::String>(methodNameStr.c_str());
  v8::Local<v8::FunctionTemplate> methodCallTemplate = NanNew<v8::FunctionTemplate>(methodCall, methodName);
  funcTemplate->PrototypeTemplate()->Set(methodName, methodCallTemplate->GetFunction());

  // Create the sync method variant, with 'Sync' appended to the method name
  v8::Handle<v8::String> methodNameSync = NanNew<v8::String>((methodNameStr + "Sync").c_str());
  v8::Local<v8::FunctionTemplate> methodCallSyncTemplate = NanNew<v8::FunctionTemplate>(methodCallSync, methodName);
  funcTemplate->PrototypeTemplate()->Set(methodNameSync, methodCallSyncTemplate->GetFunction());
}

I'd like to pass in a promisify function (written in javascript) to node-java at initialization time, along these lines:

var java = require('java');
var asyncOptions = {
  promisify: require('q').nfbind     // or require('bluebird').promisify
  suffix: 'Promise'
};
java.setAsyncOptions(asyncOptions);

I need to know how to implement the new method setAsyncOptions(asyncOptions);

Then, in the above loop I'd add code along these lines:

// Create the promise method variant, appending to the original method name the passed in suffix.
v8::Handle<v8::String> promiseSuffixStr = ???; // asyncOptions.suffix
v8::Handle<v8::String> methodNamePromise = NanNew<v8::String>(methodNameStr + promiseSuffixStr);
v8::Local<v8::FunctionTemplate> methodCallTemplate = NanNew<v8::FunctionTemplate>(methodCall, methodName);
v8::Local<v8::FunctionTemplate> promiseCallTemplate = ???; // asyncOptions.promisify(methodCallTemplate)
funcTemplate->PrototypeTemplate()->Set(methodNamePromise, promiseCallTemplate->GetFunction());

I need assistance with the two parts shown as ???.

Thanks in advance for any help.


EDIT: I've made good progress on this and have submitted a draft PR. The owner of node-java has agreed to work with me to complete it. Anyone reading this is welcome to look at the PR. I'll update this question with an answer when we have a complete solution.


Solution

  • I was able to find the complete solution, which is now merged into the node-java npm package. See PR187.