|
| 1 | +# AsyncProgressWorker |
| 2 | + |
| 3 | +`Napi::AsyncProgressWorker` is an abstract class which implements `Napi::AsyncWorker` |
| 4 | +while extending `Napi::AsyncWorker` internally with `Napi::ThreadSafeFunction` for |
| 5 | +moving work progress reports from worker thread(s) to event loop threads. |
| 6 | + |
| 7 | +Like `Napi::AsyncWorker`, once created, execution is requested by calling |
| 8 | +`Napi::AsyncProgressWorker::Queue`. When a thread is available for execution |
| 9 | +the `Napi::AsyncProgressWorker::Execute` method will be invoked. During the |
| 10 | +execution, `Napi::AsyncProgressWorker::ExecutionProgress::Send` can be used to |
| 11 | +indicate execution process, which will eventually invoke `Napi::AsyncProgressWorker::OnProgress` |
| 12 | +on the JavaScript thread to safely call into JavaScript. Once `Napi::AsyncProgressWorker::Execute` |
| 13 | +completes either `Napi::AsyncProgressWorker::OnOK` or `Napi::AsyncProgressWorker::OnError` |
| 14 | +will be invoked. Once the `Napi::AsyncProgressWorker::OnOK` or `Napi::AsyncProgressWorker::OnError` |
| 15 | +methods are complete the `Napi::AsyncProgressWorker` instance is destructed. |
| 16 | + |
| 17 | +For the most basic use, only the `Napi::AsyncProgressWorker::Execute` and |
| 18 | +`Napi::AsyncProgressWorker::OnProgress` method must be implemented in a subclass. |
| 19 | + |
| 20 | +## Methods |
| 21 | + |
| 22 | +[`Napi::AsyncWorker`][] provides detailed descriptions for most methods. |
| 23 | + |
| 24 | +### Execute |
| 25 | + |
| 26 | +This method is used to execute some tasks outside of the **event loop** on a libuv |
| 27 | +worker thread. Subclasses must implement this method and the method is run on |
| 28 | +a thread other than that running the main event loop. As the method is not |
| 29 | +running on the main event loop, it must avoid calling any methods from node-addon-api |
| 30 | +or running any code that might invoke JavaScript. Instead, once this method is |
| 31 | +complete any interaction through node-addon-api with JavaScript should be implemented |
| 32 | +in the `Napi::AsyncProgressWorker::OnOK` method and/or `Napi::AsyncProgressWorker::OnError` |
| 33 | +which run on the main thread and are invoked when the `Napi::AsyncProgressWorker::Execute` |
| 34 | +method completes. |
| 35 | + |
| 36 | +```cpp |
| 37 | +virtual void Napi::AsyncProgressWorker::Execute(const ExecutionProgress& progress) = 0; |
| 38 | +``` |
| 39 | +
|
| 40 | +### OnOK |
| 41 | +
|
| 42 | +This method is invoked when the computation in the `Execute` method ends. |
| 43 | +The default implementation runs the `Callback` optionally provided when the |
| 44 | +`AsyncProgressWorker` class was created. The `Callback` will by default receive no |
| 45 | +arguments. Arguments to the callback can be provided by overriding the `GetResult()` |
| 46 | +method. |
| 47 | +
|
| 48 | +```cpp |
| 49 | +virtual void Napi::AsyncProgressWorker::OnOK(); |
| 50 | +``` |
| 51 | + |
| 52 | +### OnProgress |
| 53 | + |
| 54 | +This method is invoked when the computation in the `Napi::AsyncProgressWorker::ExecutionProcess::Send` |
| 55 | +method was called during worker thread execution. |
| 56 | + |
| 57 | +```cpp |
| 58 | +virtual void Napi::AsyncProgressWorker::OnProgress(const T* data, size_t count) |
| 59 | +``` |
| 60 | +
|
| 61 | +### Constructor |
| 62 | +
|
| 63 | +Creates a new `Napi::AsyncProgressWorker`. |
| 64 | +
|
| 65 | +```cpp |
| 66 | +explicit Napi::AsyncProgressWorker(const Napi::Function& callback); |
| 67 | +``` |
| 68 | + |
| 69 | +- `[in] callback`: The function which will be called when an asynchronous |
| 70 | +operations ends. The given function is called from the main event loop thread. |
| 71 | + |
| 72 | +Returns a `Napi::AsyncProgressWorker` instance which can later be queued for execution by |
| 73 | +calling `Napi::AsyncWork::Queue`. |
| 74 | + |
| 75 | +### Constructor |
| 76 | + |
| 77 | +Creates a new `Napi::AsyncProgressWorker`. |
| 78 | + |
| 79 | +```cpp |
| 80 | +explicit Napi::AsyncProgressWorker(const Napi::Function& callback, const char* resource_name); |
| 81 | +``` |
| 82 | +
|
| 83 | +- `[in] callback`: The function which will be called when an asynchronous |
| 84 | +operations ends. The given function is called from the main event loop thread. |
| 85 | +- `[in] resource_name`: Null-terminated string that represents the |
| 86 | +identifier for the kind of resource that is being provided for diagnostic |
| 87 | +information exposed by the async_hooks API. |
| 88 | +
|
| 89 | +Returns a `Napi::AsyncProgressWorker` instance which can later be queued for execution by |
| 90 | +calling `Napi::AsyncWork::Queue`. |
| 91 | +
|
| 92 | +### Constructor |
| 93 | +
|
| 94 | +Creates a new `Napi::AsyncProgressWorker`. |
| 95 | +
|
| 96 | +```cpp |
| 97 | +explicit Napi::AsyncProgressWorker(const Napi::Function& callback, const char* resource_name, const Napi::Object& resource); |
| 98 | +``` |
| 99 | + |
| 100 | +- `[in] callback`: The function which will be called when an asynchronous |
| 101 | +operations ends. The given function is called from the main event loop thread. |
| 102 | +- `[in] resource_name`: Null-terminated string that represents the |
| 103 | +identifier for the kind of resource that is being provided for diagnostic |
| 104 | +information exposed by the async_hooks API. |
| 105 | +- `[in] resource`: Object associated with the asynchronous operation that |
| 106 | +will be passed to possible async_hooks. |
| 107 | + |
| 108 | +Returns a `Napi::AsyncProgressWorker` instance which can later be queued for execution by |
| 109 | +calling `Napi::AsyncWork::Queue`. |
| 110 | + |
| 111 | +### Constructor |
| 112 | + |
| 113 | +Creates a new `Napi::AsyncProgressWorker`. |
| 114 | + |
| 115 | +```cpp |
| 116 | +explicit Napi::AsyncProgressWorker(const Napi::Object& receiver, const Napi::Function& callback); |
| 117 | +``` |
| 118 | +
|
| 119 | +- `[in] receiver`: The `this` object passed to the called function. |
| 120 | +- `[in] callback`: The function which will be called when an asynchronous |
| 121 | +operations ends. The given function is called from the main event loop thread. |
| 122 | +
|
| 123 | +Returns a `Napi::AsyncProgressWorker` instance which can later be queued for execution by |
| 124 | +calling `Napi::AsyncWork::Queue`. |
| 125 | +
|
| 126 | +### Constructor |
| 127 | +
|
| 128 | +Creates a new `Napi::AsyncProgressWorker`. |
| 129 | +
|
| 130 | +```cpp |
| 131 | +explicit Napi::AsyncProgressWorker(const Napi::Object& receiver, const Napi::Function& callback, const char* resource_name); |
| 132 | +``` |
| 133 | + |
| 134 | +- `[in] receiver`: The `this` object passed to the called function. |
| 135 | +- `[in] callback`: The function which will be called when an asynchronous |
| 136 | +operations ends. The given function is called from the main event loop thread. |
| 137 | +- `[in] resource_name`: Null-terminated string that represents the |
| 138 | +identifier for the kind of resource that is being provided for diagnostic |
| 139 | +information exposed by the async_hooks API. |
| 140 | + |
| 141 | +Returns a `Napi::AsyncWork` instance which can later be queued for execution by |
| 142 | +calling `Napi::AsyncWork::Queue`. |
| 143 | + |
| 144 | +### Constructor |
| 145 | + |
| 146 | +Creates a new `Napi::AsyncProgressWorker`. |
| 147 | + |
| 148 | +```cpp |
| 149 | +explicit Napi::AsyncProgressWorker(const Napi::Object& receiver, const Napi::Function& callback, const char* resource_name, const Napi::Object& resource); |
| 150 | +``` |
| 151 | +
|
| 152 | +- `[in] receiver`: The `this` object to be passed to the called function. |
| 153 | +- `[in] callback`: The function which will be called when an asynchronous |
| 154 | +operations ends. The given function is called from the main event loop thread. |
| 155 | +- `[in] resource_name`: Null-terminated string that represents the |
| 156 | +identifier for the kind of resource that is being provided for diagnostic |
| 157 | +information exposed by the async_hooks API. |
| 158 | +- `[in] resource`: Object associated with the asynchronous operation that |
| 159 | +will be passed to possible async_hooks. |
| 160 | +
|
| 161 | +Returns a `Napi::AsyncWork` instance which can later be queued for execution by |
| 162 | +calling `Napi::AsyncWork::Queue`. |
| 163 | +
|
| 164 | +### Constructor |
| 165 | +
|
| 166 | +Creates a new `Napi::AsyncProgressWorker`. |
| 167 | +
|
| 168 | +```cpp |
| 169 | +explicit Napi::AsyncProgressWorker(Napi::Env env); |
| 170 | +``` |
| 171 | + |
| 172 | +- `[in] env`: The environment in which to create the `Napi::AsyncProgressWorker`. |
| 173 | + |
| 174 | +Returns an `Napi::AsyncProgressWorker` instance which can later be queued for execution by calling |
| 175 | +`Napi::AsyncProgressWorker::Queue`. |
| 176 | + |
| 177 | +Available with `NAPI_VERSION` equal to or greater than 5. |
| 178 | + |
| 179 | +### Constructor |
| 180 | + |
| 181 | +Creates a new `Napi::AsyncProgressWorker`. |
| 182 | + |
| 183 | +```cpp |
| 184 | +explicit Napi::AsyncProgressWorker(Napi::Env env, const char* resource_name); |
| 185 | +``` |
| 186 | +
|
| 187 | +- `[in] env`: The environment in which to create the `Napi::AsyncProgressWorker`. |
| 188 | +- `[in] resource_name`: Null-terminated string that represents the |
| 189 | +identifier for the kind of resource that is being provided for diagnostic |
| 190 | +information exposed by the async_hooks API. |
| 191 | +
|
| 192 | +Returns a `Napi::AsyncProgressWorker` instance which can later be queued for execution by |
| 193 | +calling `Napi::AsyncProgressWorker::Queue`. |
| 194 | +
|
| 195 | +Available with `NAPI_VERSION` equal to or greater than 5. |
| 196 | +
|
| 197 | +### Constructor |
| 198 | +
|
| 199 | +Creates a new `Napi::AsyncProgressWorker`. |
| 200 | +
|
| 201 | +```cpp |
| 202 | +explicit Napi::AsyncProgressWorker(Napi::Env env, const char* resource_name, const Napi::Object& resource); |
| 203 | +``` |
| 204 | + |
| 205 | +- `[in] env`: The environment in which to create the `Napi::AsyncProgressWorker`. |
| 206 | +- `[in] resource_name`: Null-terminated string that represents the |
| 207 | +identifier for the kind of resource that is being provided for diagnostic |
| 208 | +information exposed by the async_hooks API. |
| 209 | +- `[in] resource`: Object associated with the asynchronous operation that |
| 210 | +will be passed to possible async_hooks. |
| 211 | + |
| 212 | +Returns a `Napi::AsyncProgressWorker` instance which can later be queued for execution by |
| 213 | +calling `Napi::AsyncProgressWorker::Queue`. |
| 214 | + |
| 215 | +Available with `NAPI_VERSION` equal to or greater than 5. |
| 216 | + |
| 217 | +### Destructor |
| 218 | + |
| 219 | +Deletes the created work object that is used to execute logic asynchronously and |
| 220 | +release the internal `Napi::ThreadSafeFunction`, which will be aborted to prevent |
| 221 | +unexpected upcoming thread safe calls. |
| 222 | + |
| 223 | +```cpp |
| 224 | +virtual Napi::AsyncProgressWorker::~AsyncProgressWorker(); |
| 225 | +``` |
| 226 | + |
| 227 | +# AsyncProgressWorker::ExecutionProcess |
| 228 | + |
| 229 | +A bridge class created before the worker thread execution of `Napi::AsyncProgressWorker::Execute`. |
| 230 | + |
| 231 | +## Methods |
| 232 | + |
| 233 | +### Send |
| 234 | + |
| 235 | +`Napi::AsyncProgressWorker::ExecutionProcess::Send` takes two arguments, a pointer |
| 236 | +to a generic type of data, and a `size_t` to indicate how many items the pointer is |
| 237 | +pointing to. |
| 238 | + |
| 239 | +The data pointed to will be copied to internal slots of `Napi::AsyncProgressWorker` so |
| 240 | +after the call to `Napi::AsyncProgressWorker::ExecutionProcess::Send` the data can |
| 241 | +be safely released. |
| 242 | + |
| 243 | +Note that `Napi::AsyncProgressWorker::ExecutionProcess::Send` merely guarantees |
| 244 | +**eventual** invocation of `Napi::AsyncProgressWorker::OnProgress`, which means |
| 245 | +multiple send might be coalesced into single invocation of `Napi::AsyncProgressWorker::OnProgress` |
| 246 | +with latest data. |
| 247 | + |
| 248 | +```cpp |
| 249 | +void Napi::AsyncProgressWorker::ExecutionProcess::Send(const T* data, size_t count) const; |
| 250 | +``` |
| 251 | +
|
| 252 | +## Example |
| 253 | +
|
| 254 | +The first step to use the `Napi::AsyncProgressWorker` class is to create a new class that |
| 255 | +inherits from it and implement the `Napi::AsyncProgressWorker::Execute` abstract method. |
| 256 | +Typically input to the worker will be saved within the class' fields generally |
| 257 | +passed in through its constructor. |
| 258 | +
|
| 259 | +During the worker thread execution, the first argument of `Napi::AsyncProgressWorker::Execute` |
| 260 | +can be used to report the progress of the execution. |
| 261 | +
|
| 262 | +When the `Napi::AsyncProgressWorker::Execute` method completes without errors the |
| 263 | +`Napi::AsyncProgressWorker::OnOK` function callback will be invoked. In this function the |
| 264 | +results of the computation will be reassembled and returned back to the initial |
| 265 | +JavaScript context. |
| 266 | +
|
| 267 | +`Napi::AsyncProgressWorker` ensures that all the code in the `Napi::AsyncProgressWorker::Execute` |
| 268 | +function runs in the background out of the **event loop** thread and at the end |
| 269 | +the `Napi::AsyncProgressWorker::OnOK` or `Napi::AsyncProgressWorker::OnError` function will be |
| 270 | +called and are executed as part of the event loop. |
| 271 | +
|
| 272 | +The code below shows a basic example of the `Napi::AsyncProgressWorker` implementation: |
| 273 | +
|
| 274 | +```cpp |
| 275 | +#include<napi.h> |
| 276 | +
|
| 277 | +#include <chrono> |
| 278 | +#include <thread> |
| 279 | +
|
| 280 | +use namespace Napi; |
| 281 | +
|
| 282 | +class EchoWorker : public AsyncProgressWorker<uint32_t> { |
| 283 | + public: |
| 284 | + EchoWorker(Function& callback, std::string& echo) |
| 285 | + : AsyncProgressWorker(callback), echo(echo) {} |
| 286 | +
|
| 287 | + ~EchoWorker() {} |
| 288 | + // This code will be executed on the worker thread |
| 289 | + void Execute(const ExecutionProgress& progress) { |
| 290 | + // Need to simulate cpu heavy task |
| 291 | + for (uint32_t i = 0; i < 100; ++i) { |
| 292 | + progress.Send(&i, 1) |
| 293 | + std::this_thread::sleep_for(std::chrono::seconds(1)); |
| 294 | + } |
| 295 | + } |
| 296 | +
|
| 297 | + void OnOK() { |
| 298 | + HandleScope scope(Env()); |
| 299 | + Callback().Call({Env().Null(), String::New(Env(), echo)}); |
| 300 | + } |
| 301 | +
|
| 302 | + void OnProgress(const uint32_t* data, size_t /* count */) { |
| 303 | + HandleScope scope(Env()); |
| 304 | + Callback().Call({Env().Null(), Env().Null(), Number::New(Env(), *data)}); |
| 305 | + } |
| 306 | +
|
| 307 | + private: |
| 308 | + std::string echo; |
| 309 | +}; |
| 310 | +``` |
| 311 | + |
| 312 | +The `EchoWorker`'s constructor calls the base class' constructor to pass in the |
| 313 | +callback that the `Napi::AsyncProgressWorker` base class will store persistently. When |
| 314 | +the work on the `Napi::AsyncProgressWorker::Execute` method is done the |
| 315 | +`Napi::AsyncProgressWorker::OnOk` method is called and the results are return back to |
| 316 | +JavaScript when the stored callback is invoked with its associated environment. |
| 317 | + |
| 318 | +The following code shows an example of how to create and use an `Napi::AsyncProgressWorker` |
| 319 | + |
| 320 | +```cpp |
| 321 | +#include <napi.h> |
| 322 | + |
| 323 | +// Include EchoWorker class |
| 324 | +// .. |
| 325 | + |
| 326 | +use namespace Napi; |
| 327 | + |
| 328 | +Value Echo(const CallbackInfo& info) { |
| 329 | + // We need to validate the arguments here |
| 330 | + Function cb = info[1].As<Function>(); |
| 331 | + std::string in = info[0].As<String>(); |
| 332 | + EchoWorker* wk = new EchoWorker(cb, in); |
| 333 | + wk->Queue(); |
| 334 | + return info.Env().Undefined(); |
| 335 | +} |
| 336 | +``` |
| 337 | +
|
| 338 | +The implementation of a `Napi::AsyncProgressWorker` can be used by creating a |
| 339 | +new instance and passing to its constructor the callback to execute when the |
| 340 | +asynchronous task ends and other data needed for the computation. Once created, |
| 341 | +the only other action needed is to call the `Napi::AsyncProgressWorker::Queue` |
| 342 | +method that will queue the created worker for execution. |
| 343 | +
|
| 344 | +[`Napi::AsyncWorker`]: ./async_worker.md |
0 commit comments