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