1.3.8 多线程
NativeScript的好处之一就是它允许通过JS快速高效地使用所有原生平台( Android/Objective-C )的API,而不用使用串行化或者映射。然而这导致另一个纠结——所有JS都在主线程上执行 (又名: UI thread
) 。这意味着操作很可能花掉很多时间停滞于UI渲染并且让应用看起来感觉很慢。
要解决UI的清晰度和高性能带来的缓慢是至关重要的,开发者可以使用 NativeScript的解决方案处理多线程——工作者线程。工作者是在一个绝对隔离的上下文中在后台线程上执行的脚本。要花掉很多时间执行的任务应该卸装到一个工作者线程之上。
NativeScript 里的 Workers API 是松散地基于 Dedicated Web Workers API 和 Web Workers Specification 。
Workers API
- Worker Object
- Worker Global Scope
Sample Usage
Limitations
Workers API
Worker 对象原型
new Worker(path)
- 创建一个worker的一个实例,并生成一个操作系统的新线程,该脚本的指向由执行的path
参数决定。postMessage(message)
- 发送一个串行化的JSON消息关联脚本的onmessage
事件处理器。terminate()
- 终止运行下一个运行循环上的worker线程。
Worker 对象事件处理器
onmessage(message)
- 处理发送自关联的worker线程传入的消息。消息对象有如下属性:message.data
- 消息内容, 如在worker线程发送的postMessage
。
onerror(error)
- 处理从worker线程未捕获的错误。. 错误对象暴露如下属性:error.message
- 未发现的错误,一个堆栈,如果可用的话。error.filename
- 文件位置,如果抛出未捕获错误的话。error.lineno
- 行号,如果抛出未捕获错误的话。
Worker 全局范围
self
- 返回WorkerGlobalScope
本身的一个引用。postMessage(message)
- 发送一个串行化的JSON消息到主线程上的Worker 实例的onmessage
事件处理器。close()
- 终止运行下一个运行循环上的worker线程。
Worker 全局范围事件处理器
onmessage(message)
- 处理发送自主r线程传入的消息。消息对象暴露如下属性:message.data
- 消息内容, 如在主r线程发送的postMessage
。
onerror(error)
- 处理 Worker 作用域(worker线程)内部执行的函数未捕获的错误 。error
参数包含未捕获的错误。如果处理器返回一个真类的值,消息不会传播到主线程上的 Worker 实例的onerror
处理器。onerror
调用 worker 线程之后,执行不会被终止,worker仍然能够发送/接收消息。onclose()
- 处理任何“清理”工作;适用于释放资源,关闭流和套接字。
main-view-model.js
...
var worker = new Worker('./workers/image-processor');
worker.postMessage({ src: imageSource, mode: 'scale', options: options });
worker.onmessage = function(msg) {
if (msg.data.success) {
// Stop idle animation
// Update Image View
// Terminate worker or send another message
worker.terminate();
} else {
// Stop idle animation
// Display meaningful message
// Terminate worker or send message with different parameters
}
}
worker.onerror = function(err) {
console.log(`An unhandled error occurred in worker: ${err.filename}, line: ${err.lineno} :`);
console.log(err.message);
}
...
workers/image-processor.js
onmessage = function(msg) {
var request = msg.data;
var src = request.src;
var mode = request.mode || 'noop' ;
var options = request.options;
var result = processImage(src, mode, options);
var msg = result !== undefined ? { success: true, src: result } : { }
postMessage(msg);
}
function processImage(src, mode, options) {
// image processing logic
// save image, retrieve location
// return source to processed image
return updatedImgSrc;
}
// does not handle errors with an `onerror` handler
// errors will propagate directly to the main thread Worker instance
General Guidelines通用指导原则
为了最理想的结果,当使用 Workers API 时,请遵循这些准则:
- 当 worker结束任务时,使用适合的API (
terminate()
或close()
) ,始终要确保关闭 worker 线程。如果在你工作的作用域之内,你的 Worker 实例在你能终止它之前变得不能得到,你要关闭它,就只能在worker自身的脚本里通过调用close()
函数。 - Workers 不是所有性能相关问题的通用解决方案。 开启一个Worker 有它自身的开销, 而且它可能有时会比处理一个快速任务更慢。所以,在诉诸worker之前优化数据库请求,或重新考虑复杂应用逻辑。
- 线程可以访问整个原生SDK, 当 调用API时 ,nativescript 开发者必须照顾到所有的同步,这些从多个线程的调用无法保证线程安全。
Limitations限制
当使用 worker时有一些限制要记住:
- 不存在JS共享内存。这意味着你不能从任一线程获得JS的值/对象。你只能串行化对象,把它发送到另一个线程并在那里反串行化。这就是 postMessage() 函数所负责的。然而,原生对象没有这个情况。你可以从多个线程获取一个原生对象,而不用复制它,因为 runtime 将创建每个线程单独的JavaScript包装对象。请记住,当您使用非线程安全的原生API和数据时,你必须自己处理同步部分。 runtime 在原生数据访问和API调用上,不执行任何锁定或者同步逻辑。
只有串行化JSON 对象可以被 postMessage() API 发送。
- 你不能发送原生对象。意思是你不能通过 postMessage 发送原生对象,因为在大多数情况下,JSON序列化JavaScript包装一个原生对象的结果为空的-“{ }” 字面的对象。另一方面这个消息会被反序列化到一个纯粹的空的JavaScript对象。发送原生对象是我们希望在未来支持的,所以敬请关注。
- 此外,当发送循环对象时要小心,因为它们的递归节点将在序列化步骤中剥离。
没有对象传输。如果你是一个Web开发者你可能熟悉ArrayBuffer和MessagePort的传输在浏览器被支持。目前, NativeScript 没有这种对象传输的概念。
目前,你无法调试运行在worker线程的上下文中的脚本。以后会实现。
没有嵌套的worker支持。我们希望从社区获知如果这在一定程度上需要我们支持的话。