8.7 版本发布—WinterCG 兼容性第一部分
了解更多

NativeScript 的优势之一是它允许通过 JavaScript 快速有效地访问所有原生平台 (Android/Objective-C) API,无需使用(反)序列化或反射。JavaScript 在主线程(也称为 UI 线程)上执行。这意味着可能需要较长时间的操作可能会延迟 UI 的渲染并导致应用程序出现延迟。

为了解决 UI 清晰度和高性能至关重要的缓慢问题,开发人员可以使用工作线程。工作线程是在绝对隔离的上下文中后台线程上执行的脚本。应将可能需要很长时间才能执行的任务卸载到工作线程。

NativeScript 中的 Worker API 松散地基于Web Worker APIWeb Worker 规范

为了在使用 Worker API 时获得最佳效果,请遵循以下准则

  • 始终确保在工作线程完成其工作后使用相应的 API (terminate()close()) 关闭工作线程。如果 Worker 实例在您能够终止它之前在您正在使用的范围内变得无法访问,则您只能从工作线程脚本本身内部通过调用 close() 函数来关闭它。
  • Worker 不是所有性能相关问题的通用解决方案。启动 Worker 本身会产生开销,有时可能比处理快速任务慢。在求助于工作线程之前,请优化数据库查询或重新考虑复杂的应用程序逻辑。
  • 由于工作线程可以访问整个原生 SDK,因此 NativeScript 开发人员必须在调用并非保证从多个线程安全访问的 API 时注意所有同步问题。

使用工作线程

生成工作线程

创建新的工作线程很简单,您只需要使用工作线程脚本的路径(相对于当前文件,或别名路径,例如 ~/path/to/worker.ts)调用 Worker() 构造函数即可。工作线程本身可以用 JavaScript 或 TypeScript 编写。

ts
const myWorker = new Worker('./worker.ts')

向工作线程发送消息

要向工作线程发送消息,请使用 postMessage() 方法在 Worker 实例上调用。

ts
// send myMessage to the worker
myWorker.postMessage(myMessage)

接收来自工作线程的消息

要接收来自工作线程的消息,请使用 onmessage 回调在 Worker 实例上调用。

ts
// attach a message handler that will receive messages from the worker thread
myWorker.onmessage = (e) => {
  console.log('Message received from the worker thread.')
  const data = e.data // data from the worker
}

向主线程发送消息

要将消息发送回主线程,请在工作线程中使用 self.postMessage() 方法。

ts
// send the workerResult back to the main thread
self.postMessage(workerResult)

接收来自主线程的消息

要在工作线程中接收消息,请在工作线程中使用 self.onmessage 事件处理程序。

ts
// attach a message handler that will receive the messages from the main thread
self.onmessage = (e) => {
  console.log('Message received from the main thread.')
  const data = e.data // data from myMessage
}

终止工作线程

如果您需要停止执行工作线程,您可以使用 Worker 实例上的 terminate 方法从主线程终止它

ts
myWorker.terminate()

工作线程将立即被终止。

处理错误

工作线程内部的运行时错误将通过 Worker 实例上的 onerror 回调报告回主线程。

ts
myWorker.onerror = (e) => {
  console.log(
    `Error occured in the worker thread in file ${e.filename} on line ${e.lineno}`
  )
  console.log(e.message, e.stackTrace)
}

工作线程还可以通过设置 onerror 处理程序来自行处理错误,该处理程序可以将错误标记为“已处理”并阻止调用主线程上的回调。

ts
self.onerror = (e) => {
  console.log('Error occured, error:', e)

  // return true-like to stop the event from being passed onto the main thread
  return true
}

Worker API

构造函数

ts
const myWorker = new Worker(path: string)

创建 Worker 的实例并生成一个新的操作系统线程,在该线程上执行 path 参数指向的脚本。


postMessage

ts
myWorker.postMessage(message)

将可 JSON 序列化的消息发送到关联脚本的 onmessage 事件处理程序。


terminate

ts
myWorker.terminate()

在下一个运行循环滴答时终止工作线程的执行。


onmessage

ts
myWorker.onmessage = function handler(message: { data: any }) {}

处理来自关联工作线程的传入消息。注意您负责创建和设置处理程序函数。

message 对象中的 data 是工作线程使用 postMessage 发送的可 JSON 序列化的消息。


onerror

ts
myWorker.onerror = function handler(error: {
  message: string // the error message
  stackTrace?: string // the stack trace if applicable
  filename: string // the file where the uncaught error was thrown
  lineno: number // the line where the uncaught error was thrown
}) {}

处理来自工作线程的未捕获错误。注意您负责创建和设置处理程序函数。


Worker 全局作用域

每个工作线程都有自己的全局作用域(因此工作线程中的 global.foo 与主线程上的 global.foo 不相同)。

self

ts
self === global // true

返回对 WorkerGlobalScope 本身的引用 - 也可作为 global 使用


postMessage

ts
self.postMessage(message)

将可 JSON 序列化的消息发送到主线程上 Worker 实例的 onmessage 事件处理程序。


close

ts
self.close()

在下一个运行循环滴答时终止工作线程的执行。


onmessage

ts
self.onmessage = function handler(message: { data: any }) {}

处理来自主线程的传入消息。

message 对象中的 data 是主线程使用 postMessage 发送的可 JSON 序列化的消息。


onerror

ts
self.onerror = function handler(error: Error): boolean {
  return true
}

处理在 Worker 作用域(工作线程)内执行函数期间发生的未捕获错误。

如果处理程序返回 true 类值,则消息将不会传播到主线程上 Worker 实例的 onerror 处理程序。

在工作线程中调用 onerror 后,执行不会终止,并且工作线程仍然能够发送/接收消息。


onclose

ts
self.onclose = function handler() {
  // do cleanup work
}

处理任何“清理”工作。适用于释放资源、关闭流和套接字。


工作线程示例

注意

为了使用 setTimeoutsetInterval 或来自 @nativescript/core 的其他全局变量,您需要将它们包含在工作线程脚本中

ts
import '@nativescript/core/globals'
ts
// main-view-model.js

const worker = new Worker('./workers/image-processor')

// send a message to our worker
worker.postMessage({ src: imageSource, mode: 'scale', options: options })

// handle incoming messages from the worker
worker.onmessage = function (message) {
  if (message.data.success) {
    // the src received from the worker
    const src = message.data.src

    // terminate worker or send another message...
    worker.terminate()
  } else {
    // handle unsuccessful task
  }
}

// handle worker errors
worker.onerror = function (err) {
  console.log(
    `An unhandled error occurred in worker: ${err.filename}, line: ${err.lineno} :`,
    err.message
  )
}
ts
// workers/image-processor.js

// load NativeScript globals in the worker thread
import '@nativescript/core/globals'

self.onmessage = function (message) {
  const src = message.data.src
  const mode = message.data.mode || 'noop'
  const options = message.data.options

  const result = processImage(src, mode, options)

  if (result) {
    // send the result back to the main thread
    self.postMessage({
      success: true,
      src: result,
    })

    return
  }

  // no result, send back an empty object for example
  self.postMessage({})
}

// example heavy function to process an image
function processImage(src, mode, options) {
  console.log(options)
  // image processing logic
  // save image, retrieve location
  // return source to processed image
  return updatedImgSrc
}

限制

在使用工作线程时,需要牢记某些限制

  • 没有 JavaScript 内存共享。这意味着您无法从两个线程访问 JavaScript 值/对象。您只能序列化对象,将其发送到另一个线程并在那里反序列化它。这就是 postMessage() 函数负责的功能。但是,对于原生对象并非如此。您可以从多个线程访问原生对象,而无需复制它,因为运行时将为每个线程创建一个单独的 JavaScript 包装器对象。请记住,当您使用非线程安全的原生 API 和数据时,您必须自行处理同步部分。运行时不会对原生数据访问和 API 调用执行任何锁定或同步逻辑。
  • 只能使用 postMessage() API 发送可 JSON 序列化的对象.
    • 您不能发送原生对象。这意味着您不能使用 postMessage 发送原生对象,因为在大多数情况下,原生对象的 JavaScript 包装器的 JSON 序列化会导致空对象文字 - "{}"。另一方面,此消息将被反序列化为纯空 JavaScript 对象。发送原生对象是我们将来想要支持的功能。
    • 发送循环对象时要小心,因为它们的递归节点将在序列化步骤中被剥离。
  • 没有对象传输。如果您是 Web 开发人员,您可能熟悉浏览器中的 ArrayBuffer 和 MessagePort 传输支持。目前,在 NativeScript 中,没有像对象传输这样的概念。
  • 目前无法调试工作线程。
  • 不支持嵌套工作线程。我们希望了解社区是否需要我们支持此功能。