How to create async function using NAPI that return Promises
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Node-API lets native addons expose asynchronous operations without blocking the event loop. If you want the JavaScript side to use await, the standard pattern is to create a promise, schedule async work, and resolve or reject that promise in the completion callback.
The Core Node-API Pattern
An async promise-returning function usually needs these steps:
- create a promise and keep the deferred handle
- create an async work item
- do the heavy work in the execute callback
- resolve or reject the deferred promise in the complete callback
- clean up the work item and native data
The central APIs are napi_create_promise, napi_create_async_work, napi_queue_async_work, napi_resolve_deferred, and napi_reject_deferred.
Complete Example
The example below exposes doAsyncWork(value) and returns a JavaScript promise:
On the JavaScript side:
Minimal binding.gyp
To build the addon with node-gyp, keep the build config simple:
Then run:
Error Handling Matters
There are two different failure paths:
- Node-API call fails immediately
- async work runs but produces an application error
The example handles the second path by storing an error string in AsyncData and rejecting the promise in Complete. In real code, you should also check the return status of each Node-API call and bail out early if napi_ok is not returned.
Keep Heavy Work Out of the Completion Callback
The execute callback runs on a worker thread. The complete callback runs back on the main JavaScript thread. That means:
- CPU-heavy native work belongs in
Execute - JavaScript value creation and promise resolution belong in
Complete
Mixing those responsibilities is a common source of crashes or event-loop stalls.
Common Pitfalls
- Returning a promise but forgetting to store the
napi_deferredhandle needed to resolve it later. - Creating JavaScript values in the worker-thread execute callback.
- Forgetting to delete the async work item and native context after completion.
- Blocking inside the main-thread completion callback and defeating the purpose of async work.
- Skipping Node-API status checks and making failures much harder to diagnose.
Summary
- Create a promise first with
napi_create_promise. - Schedule background work with
napi_create_async_workandnapi_queue_async_work. - Do native computation in the execute callback, not on the main thread.
- Resolve or reject the deferred promise in the completion callback.
- Always clean up the async work handle and any native context you allocated.

