你可能有一个项目,其中有一段长时间运行的 C/C++ 代码,你希望在后台运行它,而不是在 Node 的主事件循环中运行。Node-API 的 AsyncWorker 类专门为这种情况设计。
🌐 You may have a project in which you have a piece of long-running C/C++ code that you want to run in the background instead of on Node's main event loop. Node-API's AsyncWorker class is designed specifically for this case.
作为程序员,你的工作本质上是继承 AsyncWorker 并实现 Execute 方法。你可能还会实现一个封装函数,以便更容易使用你的 AsyncWorker。
🌐 As a programmer, your job is essentially to subclass AsyncWorker and to implement the Execute method. You'll also probably implement a wrapper function to make using your AsyncWorker easier.
在这个例子中,我们将创建一个继承自 AsyncWorker 的 SimpleAsyncWorker 类。这个工作者将接受一个整数值,表示它要“工作”的时间长度。当工作者完成工作时,它将返回一个文本字符串,指明它工作了多长时间。在一种情况下,工作者会指示一个错误。
🌐 In this example, we're going to create a SimpleAsyncWorker class that subclasses AsyncWorker. The worker will take an integer value indicating the length of time it is to "work." When the worker completes, it will return a text string indicating how long it worked. In one case, the worker will indicate an error instead.
SimpleAsyncWorker.h 是 SimpleAsyncWorker 的 C++ 头文件。它声明了一个构造函数,该构造函数接收一个参数,即 SimpleAsyncWorker 运行的时间长度(以秒为单位)。声明了一个私有数据成员来保存该值。
该头文件还声明了两个方法,Execute 和 OnOK,它们重写了 AsyncWorker 声明的方法,下面将更详细地描述它们。
🌐 The header also declares two methods, Execute and OnOK, which override methods declared by AsyncWorker, and are described in more detail below.
SimpleAsyncWorker.cc 是 C++ 实现。构造函数接受两个参数。callback 是在 Execute 方法返回时被调用的 JavaScript 函数。无论是否发生错误,callback 都会被调用。第二个构造函数参数 runTime 是一个整数值,表示工作线程运行的时间(以秒为单位)。
Node 将在一个与运行 Node 主事件循环的线程分开的线程中运行 Execute 方法的代码。Execute 方法_无法访问_ Node-API 环境的任何部分。因此,构造函数将 runTime 输入值存储为私有数据成员。
🌐 Node will run the code of the Execute method in a thread separate from the thread running Node's main event loop. The Execute method has no access to any part of the Node-API environment. For this reason, the runTime input value was stored by the constructor as a private data member.
在此实现中,Execute 方法仅等待之前由 runTime 指定的秒数。在实际实现中,这里是长时间运行代码的地方。为了演示错误处理如何工作,当 Execute 方法被要求运行 4 秒时,它会声明一个错误。
🌐 In this implementation, the Execute method simply waits the number of seconds specified earlier by runTime. This is where the long running code goes in a real implementation. To demonstrate how error handling works, this Execute method declares an error when requested to run 4 seconds.
OnOK 方法在 Execute 方法返回后被调用,除非 Execute 方法调用了 SetError,或者在启用 C++ 异常并抛出异常的情况下。发生错误时,将调用 OnError 方法而不是 OnOK。默认的 OnError 实现只是用错误作为唯一参数调用 AsyncWorker 回调函数。
🌐 The OnOK method is called after the Execute method returns unless the Execute method calls SetError or in the case where C++ exceptions are enabled and an exception is thrown. In the case of an error, the OnError method is called instead of OnOK. The default OnError implementation simply calls the AsyncWorker callback function with the error as the only argument.
在此实现中,OnOK 方法构造一个字符串值,并将其作为构造函数中指定的 callback 函数的第二个参数传递。传递给 callback 函数的第一个参数是一个 JavaScript null 值。这样做的原因是,无论是否发生错误,都会调用同一个回调函数。默认的 OnError 方法(SimpleAsyncWorker 未覆盖)会将错误作为第一个参数传递给回调函数。在下一节中这点会更加清楚。
🌐 In this implementation, the OnOK method formulates a string value and passes it as the second argument to the callback function specified in the constructor. The first argument passed to the callback function is a JavaScript null value. The reason for this is that a single callback function is called whether an error occurs or not. The default OnError method, which SimpleAsyncWorker does not override, passes the error as the first argument to the callback. This will become more clear in the next section.
请注意,与 Execute 不同,OnOK 和 OnError 方法确实可以访问 Node-API 环境。
🌐 Note that unlike Execute, the OnOK and OnError methods do have access to the Node-API environment.
我们需要一个 C++ 函数来实例化 SimpleAsyncWorker 对象并请求将它们加入队列。这个函数需要注册到 Node-API,以便可以从 JavaScript 代码中访问。RunSimpleAsyncWorker.cc 提供了这个封装。
🌐 We need a C++ function that instantiates SimpleAsyncWorker objects and requests them to be queued. This function needs to be registered with Node-API so that it is accessible from the JavaScript code. RunSimpleAsyncWorker.cc provides this wrapper.
runSimpleAsyncWorker 函数可以从 JavaScript 访问,它接收两个通过 info 参数传递的参数。第一个参数以 info[0] 形式传递,是 runTime,第二个参数是 JavaScript 回调函数,当 Execute 方法返回时会被调用。
🌐 The runSimpleAsyncWorker function, which is accessible from JavaScript, takes two arguments which are passed through the info argument. The first argument, which is passed as info[0], is the runTime and the second argument is the JavaScript callback function which gets called when the Execute method returns.
然后代码实例化一个 SimpleAsyncWorker 对象,并请求将其排入队列,以便在下一次时钟周期可能执行。除非 SimpleAsyncWorker 对象被排入队列,否则其 Execute 方法将永远不会被调用。
🌐 The code then instantiates a SimpleAsyncWorker object and requests that it be queued for possible execution on the next tick. Unless the SimpleAsyncWorker object is queued, its Execute method will never be called.
一旦 SimpleAsyncWorker 对象被加入队列,runSimpleAsyncWorker 会生成一个文本字符串并将其返回给调用者。
🌐 Once the SimpleAsyncWorker object is queued, runSimpleAsyncWorker formulates a text string and returns it to the caller.
🌐 Running in JavaScript
Test.js 是一个简单的 JavaScript 程序,展示了如何运行 SimpleAsyncWorker 实例。它调用 runSimpleAsyncWorker 三次,每次使用不同的 runTime 参数。每次调用都指定 AsyncWorkerCompletion 作为回调函数。
AsyncWorkerCompletion 函数的编码目的是处理 Execute 方法报告错误以及未报告错误的情况。它在被调用时只是简单地记录到控制台。
🌐 The AsyncWorkerCompletion function is coded to handle the cases where the Execute method reports an error and when it does not. It simply logs to the console when it's called.
当 JavaScript 成功运行时,输出如下:
🌐 Here's what the output looks like when the JavaScript successfully runs:
runSimpleAsyncWorker returned 'SimpleAsyncWorker for 2 seconds queued.'.
runSimpleAsyncWorker returned 'SimpleAsyncWorker for 4 seconds queued.'.
runSimpleAsyncWorker returned 'SimpleAsyncWorker for 8 seconds queued.'.
SimpleAsyncWorker returned 'SimpleAsyncWorker returning after 'working' 2 seconds.'.
SimpleAsyncWorker returned an error: [Error: Oops! Failed after 'working' 4 seconds.]
SimpleAsyncWorker returned 'SimpleAsyncWorker returning after 'working' 8 seconds.'.
正如预期的,每次调用 runSimpleAsyncWorker 会立即返回。当每个 SimpleAsyncWorker 完成时,AsyncWorkerCompletion 函数会被调用。
🌐 As expected, each call to runSimpleAsyncWorker immediately returns. The AsyncWorkerCompletion function gets called when each SimpleAsyncWorker completes.
🌐 Caveats
-
绝对有必要确保
Execute方法不调用任何 Node-API。这意味着Execute方法无法访问 JavaScript 代码传递的任何输入值。通常,
AsyncWorker类的构造函数会从 JavaScript 对象中收集所需的信息,并将这些信息的 副本 作为数据成员存储。然后,Execute方法的结果可以在OnOK方法中转换回 JavaScript 对象。 -
Node 进程会意识到所有正在运行的
Execute方法,并且在所有正在运行的Execute方法返回之前不会终止。 -
可以安全地通过在主线程中调用
AsyncWorker::Cancel来终止 AsyncWorker。