使用 Web Worker 实现简单的非阻塞异步

之前的文章提到了 JavaScript 中的异步编程,然而无论早就存在的还是 ES6 中的,它们都是阻塞 异步,执行函数的时候,会阻塞线程。只会把一个函数延后执行,但还是在主线程中执行,执行函数的时候会阻塞线程。换句话说,只实现了过程间并发(concurrent)而未实现并行(parallel)。

ES 规范并没有定义多线程,Node.js 至今也没有原生的多线程实现。然而在 HTML5 中却定义了 Web Worker 用于实现浏览器中的多线程。

Web Worker

引用 MDN 原文:

Web Workers 使得一个Web应用程序可以在与主执行线程分离的后台线程中运行一个脚本操作。这样做的好处是可以在一个单独的线程中执行费时的处理任务,从而允许主(通常是UI)线程运行而不被阻塞/放慢。

与朴素(原始)的多线程编程方式不同,Web Worker 通常不允许线程间共享数据,所以没有线程同步、数据竞争等问题,更没有没有锁(Mutex)和条件变量(Condition variable)等概念(注 1)。它们使用 postMessage 相互通信,可以认为是 JS 中的参与者模式实现。各个 Worker 间数据独立,不共享内存:postMessage 始终通过结构化克隆的方式深拷贝传值。

使用 Web Worker 也非常简单,只需要预先在 Worker 中注册 message 事件,在主线程中 postMessage 给 Worker 处理就好了。处理完后可以再通过 postMessage 传结果给主线程。

需要注意的是,Web Worker 中不可以操作 DOM,一切与 DOM 操作相关的函数、类都不能使用(创建一个 DOM 元素发回给主线程 appendChild 也不行),所以可以使用的方法非常有限,只适用于处理数据(注 2)。

使用 Web Worker 实现非阻塞的 Promise

前面提到 Promise 是阻塞异步,那是否可以把要处理的数据转发给某个 Worker 处理并返回一个 Promise,在处理完后将其 resolve 掉呢?

答案当然是可以的,而且实现并不复杂

1

创建Web Worker

首先当然是 new 一个 Worker 出来。需要注意的是 Worker 的构造函数 接受的是一个 JavaScript 脚本的 URL,可否接受 data-uri 看浏览器,实测 Chrome、Firefox 可以,Safari、Edge 不行(会抛异常)。

简单起见,这里还是采取 data-uri 的形式。考虑可移植性的话可以先指定一个静态文件,然后使用 postMessage 把函数体传过去。

Worker 中做了两件事:

定义一个函数变量,其值是需要执行的函数。如果 fn 本身是一个函数对象,这里将其转换为字符串,相当于把函数的源代码拼到了字符串里。

绑定 message 事件。将传入的值作为参数列表调用,然后将的返回值通过 postMessage 传给主函数。

2

当接受请求时,派发事件给创建的Worker

返回一个 Promise。注意这里不能只是简单的 postMessage。因为如果使用者多次调用 dispatch 函数一次创建了多个 Promise,之后很难确定是哪个 Promise 完成了。这里通过一个队列记忆创建的 Promise 顺序,然后依次 resolve(单个 Worker 处理 message 事件还是顺序执行的)。当然你也可以多传一个标记值给 Worker 用于标记被 resolve 的 Promise。

JavaScript 里的队列就是数组:

3

接收Worker处理完返回的值

onmessage 表示正常返回;onerror 表示出现了异常。对应的 Promise 的 resolve 和 reject 直接从队列里取出来。

完整代码

这就是完整代码了,总共不到 20 行。使用的话也很简单:

在浏览器中测试,会生成这样一段代码:

排序大数组 1000 次的同时 UI 响应仍然不受影响。

这里还有一个线程池的版本,可以创建多个 Worker 同时并行执行多个任务:

https://github.com/CarterLi/ThreadPool.js/blob/gh-pages/index.ts

因为要区分究竟是哪个 Worker 完成运行,处理 Worker 返回值的逻辑复杂了一些,有什么建议欢迎提出。

注 1:ES2017 中加入后已经可以在主线程和各 Web Worker 间共享数据,使用和还可以实现传统意义上的锁和条件变量。但由于其出现较晚且并非使用 Web Worker 的主流方式,这里不展开讨论。

注 2:还有一个可能是在 Worker 中画图,见 OffscreenCanvas。一旦实现,对游戏编程是个不小的帮助。

夏洛克 AIOps

Make Data Think

人工智能 机器学习 IT运维

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20180105G0903A00?refer=cp_1026

相关快讯

扫码关注云+社区