• Web Worker,JS多线程,vueworker


    http://www.ruanyifeng.com/blog/2018/07/web-worker.html 阮一峰文档介绍web worker

    摘录:

    一、概述
    JavaScript 语言采用的是单线程模型,也就是说,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着。随着电脑计算能力的增强,尤其是多核 CPU 的出现,单线程带来很大的不便,无法充分发挥计算机的计算能力。

    Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。

    Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭

    Web Worker 有以下几个使用注意点。
    (1)同源限制

    分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。

    (2)DOM 限制

    Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。

    (3)通信联系

    Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。

    (4)脚本限制

    Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。

    (5)文件限制

    Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。

    应用场景
    本人使用的vue开发,在使用echarts图表时,后端返回数据量很大,又要对数据进行遍历操作,还有图表比较,需要将多张图的数据拼合排序比对,数据很多,操作量很大,会造成页面卡死,失去相应,遂使用web worker。

    下面来看看vue中使用worker
    git官方文档:https://github.com/israelss/vue-worker

    https://blog.csdn.net/weixin_34080903/article/details/85999611?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase 原博客。介绍了vue worker

    摘录:
    安装:

    yarn add vue-worker

    // or

    npm install vue-worker --save

    配置:

    在main.js中:

    import VueWorker from 'vue-worker';
    Vue.use(VueWorker);
    这会将属性注入Vue(并将其传递给每个子组件),默认名称为$worker,可以this.$worker在任何Vue组件内部使用该默认名称进行访问。

    您可以在注册插件时更改该名称:

    import VueWorker from 'vue-worker'
    Vue.use(VueWorker, '$desired-name')
    使用


    export default {
    name: 'worker-test',
    data() {
    return {
    worker: null,
    }
    },
    mounted() {
    // 通过this.$worker.run这个方法,跑起一个worker,
    // worker是在另外的线程里面跑的,所以可以在run的第一个参数函数里面执行一个非常大计算的操作
    // run方法像Promise一样提供.then和.catch,then的参数就是run第一个参数函数的返回值
    this.worker = this.$worker.run(n => n + 10, [2])
    // 数组中为传递给worker的参数,可以传递多个,此案例为2
    .then(res => console.log(res)) // res为worker计算结束return返回的结果数据,可以在.then里根据返回结果继续操作主线程后续任务
    .catch(e => console.log(e)) // 报错信息
    },
    destroyed() {
    // 通过赋值null的方式,释放掉worker引用,这样就可以关闭worker,这是作者在github上说的
    this.worker = null
    },
    }
    API介绍
    下面来详细介绍一下vue-worker的几个api,也就是方法method。

    .run(fun, [...args])

    上面已经看到了这个方法,而且也有注释说明。注意第二个参数是一个数组,数组的个数和第一个参数fun的形参个数是一样的。 你可能会有一个疑问:直接在fun函数体内使用当前变量不就好了么?js本身的全局变量特性在这里不能用?这是因为worker是在另外一个线程中运行,跟当前页面内的js脚本不是在同一个线程,不共享内存空间,所以直接在fun函数体里面使用另外一个线程的变量是找不到的,所以要通过函数参数的形式进行传递。而传递的实质,是使用了worker的postMessage方法,把第二个参数当做postMessage的内容,具体你可以阅读这里的源码。

    run是一次性的,跑完这次,worker线程就会被关掉。想要持久化worker,可以使用下面的create来创建。

    .create([...actions])

    这个方法让你创建一个worker对象(注意不是worker实例,你无法通过该对象直接操作worker,这个实例仅仅是一个js object,提供了几个属性接口)。

    actions是一个数组,数组的每个元素是一个含有两个属性的对象:

    export default {
    name: 'worker-test',
    data() {
    return {
    worker: null,
    }
    },
    created() {
    this.worker = this.$worker.create([
    {
    message: 'pull-data',
    func(data) {
    data.forEach(...)
    return data
    },
    },
    {
    message: 'run-task',
    func(id) {
    //...
    },
    }
    ])
    },
    mounted() {
    let data = ...
    this.worker.postMessage('pull-data', [data])
    .then(res => console.log(res))
    },
    destroyed() {
    this.worker = null
    },
    }
    实际上.run方法是create方法和postMessage方法的合体,一次性把两个方法的事都做了。

    .postMessage(messageid, [...args])

    这个在上面的代码里面已经演示了。它不是this.$worker的方法,而是通过this.$worker.create之后得到的object的一个方法。使用这个方法跟worker原生的方法很像,当然,这里的messageid就是上面actions数组里面的某个对象的message字段对应的那个。而args就是你要传递的数据。

    你可能又会问了,这里的[..args]是一个参数,还是说里面的元素才是参数。其实很简单,[...args]被用作了.apply的第二个参数:func.apply(null, [...args]),所以,...args对应的就是func的参数。

    .postAll([...args])

    这里的postAll和上面的postMessage一样,是create之后的那个object的一个方法,而不是this.$worker的,所以使用的时候,也只能用在create之后。

    它的参数是一个数组,但是这个数组的元素有三种形式,一种是不传,一种是string:messageid,另一种是{message, [...func_args]}。其实都很好理解。

    不传

    代表所有的actions都执行一次postMessage。

    [string:messageid]

    代表对应的messageid的那个action被执行postMessage。

    [{message, [...func_args]}]

    给指定的messageid传参数。

    export default {
    name: 'worker-test',
    data() {
    return {
    worker: null,
    }
    },
    created() {
    this.worker = this.$worker.create([
    {
    message: 'pull-data',
    func(data) {
    data.forEach(...)
    return data
    },
    },
    {
    message: 'run-task',
    func() {
    //...
    },
    }
    ])
    },
    mounted() {

    // 1. 不传
    this.worker.postAll().then([res1, res2] => {})

    // 2. 字符串形式
    let data = ...
    this.worker.postAll(['run-task']).then([res] => {}) // 仅'run-task'被postMessage

    // 3. 对象形式(混合形式)
    this.worker.postAll([
    'run-task',
    {
    message: 'pull-data',
    args: [data],
    },
    ]).then([res1, res2] => {})
    },
    destroyed() {
    this.worker = null
    },
    }
    比较难把握的就是,这里所有的传入都要采用数组的形式,理解上需要稍微思考下。

    .register(action || [...actions])

    同理,也是在<worker>的object对象上的方法。当你使用create之后,发现你的worker任务不够用,要追加一个action或多个,那么可以使用register来追加。action(s)和create是一模一样的。

    .unregister(message || [...messages]) 和register有点像,意思是当你某一个任务不想要了,可以通过unregister来取消这个任务。参数和register不一样,直接使用messageid作为参数即可。

    export default {
    name: 'worker-test',
    data() {
    return {
    worker: null,
    }
    },
    created() {
    this.worker = this.$worker.create([
    {
    message: 'pull-data',
    func(data) {
    data.forEach(...)
    return data
    },
    },
    {
    message: 'run-task',
    func() {
    //...
    },
    }
    ])
    },
    mounted() {
    // 1. 不传
    this.worker.postAll().then([res1, res2] => {})

    // 2. 字符串形式
    let data = ...
    this.worker.postAll(['run-task']).then([res] => {}) // 仅'run-task'被postMessage

    // 3. 对象形式(混合形式)
    this.worker.postAll([
    'run-task',
    {
    message: 'pull-data',
    args: [data],
    },
    ]).then([res1, res2] => {
    // 注意,这里then里面执行的是在主js线程里面执行的,所以可以直接用this.worker
    this.worker.unregister('run-task')
    // 当你注销掉了,那么下回你在post到run-task这个任务消息时,就啥都不会发生了
    })
    },
    destroyed() {
    this.worker = null
    },
    }
    关闭worker

    在最前面的代码里面已经提到了,插件的作者指出,你是没办法拿到worker原始实例的,所以也就无法调用worker.terminate()或者在worker线程内部执行self.close()来关闭worker。create方法创建的不是worker实例,所以它内部有,但是没有暴露出来。所以插件没有关闭worker的方法,你直接把worker对象释放掉即可。我翻阅了源码,发现它只在调用run方法时才使用close,执行完run之后worker会被close,但是如果你使用create创建的worker,是不会被close的它会一直存在,直到你关闭浏览器。

    原理

    web worker是通过一个浏览器提供的Worker对象来创建的,创建的时候要传入指定的javascript文件作为worker线程的执行脚本。worker线程内的脚本有一些限制,比如只能拿到window.navigator的信息,不能拿到完整的window对象。重点是,这里我们没有提供一个js文件传入worker线程,vue-worker是怎么做到的呢?它利用了Blob来创建一个可执行的二进制上下文,在通过这个上下文来调用我们传入的function,就好像在内存中虚拟了一个内容是我们传入的function的js文件一样。


    ————————————————
    版权声明:本文为CSDN博主「大德大威顾神」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/q759859479/article/details/106429900

  • 相关阅读:
    Action中使用Json
    QueryHelper插件类(hql)
    StrutsResultSupport的使用
    使用POI操作Excel
    对于response.setContentType(MIME)的解释
    通过反射技术获取泛型真实实例
    spring中context:property-placeholder
    SSH整合需要的jar包
    SSH整合主要XML代码
    设置三思LED的IP地址跟端口号
  • 原文地址:https://www.cnblogs.com/javalinux/p/15633386.html
Copyright © 2020-2023  润新知