javascriptnode.jsasynchronouspromisen-api

How to create async function using NAPI that return Promises


i am trying to create node module using NAPI .I have to create async function that returns promises. I don't want that testasynfunction will block NodeJS event loop. do_something_asynchronous is a synchronous function.

napi_deferred do_something_synchronous(napi_env env,napi_deferred deferred){
  printf("\n3) Function called");
  //napi_deferred deferred;
  napi_value undefined;
  napi_status status;

  // Create a value with which to conclude the deferred.
  status = napi_get_undefined(env, &undefined);
  if (status != napi_ok) return NULL;
  sleep(5);
  // Resolve or reject the promise associated with the deferred depending on
  // whether the asynchronous action succeeded.
  if (false) {
    printf("\n5) Success\nXXXXXXX");
    status = napi_resolve_deferred(env, deferred, undefined);
  } else {
    printf("\nReject");
    status = napi_reject_deferred(env, deferred, undefined);
  }
  if (status != napi_ok) return NULL;

  // At this point the deferred has been freed, so we should assign NULL to it.
  deferred = NULL;
}

//Function will be called from the js 
napi_value testasynfunction(napi_env env, napi_callback_info info){
  printf("XXXXX Hello \n");
  napi_deferred deferred;
  napi_value promise;
  napi_status status;
  // Create the promise.
  status = napi_create_promise(env, &deferred, &promise);
  if (status != napi_ok) return NULL;
  printf("\n1) Calling function to do something");
  do_something_synchronous(env,deferred);
  //std::async(do_something_asynchronous,env,deferred);
  printf("\n2) Returning Promise");
  return promise;
}
napi_property_descriptor testasync = DECLARE_NAPI_METHOD("testasyn", testasynfunction);
  status = napi_define_properties(env, exports, 1, &testasync);
  assert(status == napi_ok);

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

Question : 1) How can i run do_something_synchronous in asynchronously so that nodejs event loop will not get blocked and return promises ?


Solution

  • The following code snippet is the key component of this asynchronously operations. The napi_create_async_work() function allocates a work object where we can specify the worker handler function, say in our case ExecuteMyPromise1() (long running or process heavy task can be deployed with it). This function will be queue for worker pool thread and it will get executed asynchronously in parallel with the node.js main event loop thread.

    So far all looks OK, the problem will come when ExecuteMyPromise1 function plan to exchange result with JavaScript layer directly from the worker thread. Any JavaScript operation can normally only be called from a native addon's main thread. Then for this result exchange we have to use another native function that will be called from main thread. That is why we are using CompleteMyPromise1 function handler (if any final cleanup activity that also done from this function). This native function will be called from the main event loop thread when the asynchronous logic is completed or is cancelled.

    // The function called by javascript to get a promise returned.
    napi_value MyPromise1(napi_env env, napi_callback_info info)
    {
    
      // -- -- -- --
      // -- -- -- --
    
      // Create a promise object.
      status = napi_create_promise(env, &promDataEx->deferred, &promise);
      if (status != napi_ok)
      {
        napi_throw_error(env, NULL, "Unable to create promise.");
      }
    
    
      // -- -- -- --
      // -- -- -- --
    
      {
        // Create the async function.
        napi_value resource_name;
        napi_create_string_utf8(env, "MyPromise1", -1, &resource_name);
        napi_create_async_work(env, NULL, resource_name, 
            ExecuteMyPromise1, CompleteMyPromise1, 
            promDataEx, &promDataEx->work);
        napi_queue_async_work(env, promDataEx->work);
      }
    
    
      return promise;
    }
    
    // Execute the asynchronous work.
    void ExecuteMyPromise1(napi_env env, void *data)
    {
        prom_data_ex_t *promDataEx = (prom_data_ex_t *)data;
    
        // Calculate prime count
        promDataEx->PrimeCount = CPrimeCount( promDataEx->x, promDataEx->y );
    
        // Set the status as asynchronous_action is success
        promDataEx->asynchronous_action_status = 0;
        //sleep(3);
    }
    
    // Handle the completion of the asynchronous work.
    void CompleteMyPromise1(napi_env env, napi_status status, void *data)
    {
        napi_value rcValue;
    
        prom_data_ex_t *promDataEx = (prom_data_ex_t *)data;
    
        napi_create_int32(env, promDataEx->PrimeCount, &rcValue);
    
        // Resolve or reject the promise associated with the deferred depending on
        // whether the asynchronous action succeeded.
        if ( promDataEx->asynchronous_action_status == 0) // Success
        {
            status = napi_resolve_deferred(env, promDataEx->deferred, rcValue);
        }
        else
        {
            napi_value undefined;
    
            status = napi_get_undefined(env, &undefined);
            status = napi_reject_deferred(env, promDataEx->deferred, undefined );
        }
        if (status != napi_ok)
        {
            napi_throw_error(env, NULL, "Unable to create promise result.");
        }
    
        napi_delete_async_work(env, promDataEx->work);
        free(promDataEx);
    }
    

    Here the full example code for it https://github.com/msatyan/MyNodeC/blob/master/src/mync1/MyPromise1.cpp

    The JavaScrip call is TestPromiseWithAsync() https://github.com/msatyan/MyNodeC/blob/master/test/TestExtensions.js