【worker】js中的多线程

简介: 因为下个项目中要用到一些倒计时的功能,所以就提前准备了一下,省的到时候出现一下界面不友好和一些其他的事情。正好趁着这个机会也加深一下html5中的多线程worker的用法和理解。 Worker简介     JavaScript 语言采用的是单线程模型,也就是说,所有任务只能在一个线程上完成,一次只能做一件事。

      因为下个项目中要用到一些倒计时的功能,所以就提前准备了一下,省的到时候出现一下界面不友好和一些其他的事情。正好趁着这个机会也加深一下html5中的多线程worker的用法和理解。

Worker简介

    JavaScript 语言采用的是单线程模型,也就是说,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着。这些都是我们所公知的。但是随着业务的不断增加,只是单纯的单线程模式已经可能无法满足我们的需求了。于是在html5中新增了后台任务worker API。

w3c中的介绍:web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。您可以继续做任何愿意做的事情:点击、选取内容等等,而此时 web worker 在后台运行。

       worker就是为了JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。开启后台线程,在不影响前台线程的前提下做一些耗时或者异步的操作。因为是不同的线程,所以主线程与worker线程互不干扰。也不会相互打断。所以在一些场景可以提高页面的流程性。Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。

使用规则

  1. 必须同源:也就是说js文件的路径必须和主线程的脚本同源。防止了外部引用。
  2. dom限制:在worker线程中不能操作dom(document,window,parent)。注意可以使用浏览器的navigator和location对象。
  3. 通讯限制:worker线程和主线程不在一个上下文中所以不能直接通讯。也就是说主线程定义的变量在worker中也是不能使用的。所有只能通过消息完成。
  4. 提示禁止:worker线程不能alert和confirm,这个不知到具体原因?
  5. 传值dom:进行消息通讯也不能传值dom只能是变量。
  6. ie限制:ie9不能使用!ie9不能使用!ie9不能使用!

worker文档

Web Workers API的Worker界面代表了一个可以轻松创建的后台任务,可以将消息发送回其创建者。创建worker就像调用 构造函数并指定要在工作线程中运行的脚本一样简单。

构造函数

worker():创建一个专用的Web worker,在指定的URL上执行脚本。示例:var worker=new Worker('js/setTime.js');

属性

onerror:

这是一个在error事件发生时调用的函数,并且通过该函数冒泡worker。示例:worker.onerror=function(){....};

onmessage

  这是一个worker中message事件要发生的时候调用的事件。  示例:worker.onmessage=function(){....};

  这个事件一般与postMessage事件同时使用,一个用来发送数据,一个用来接受数据。例如:

 主线程中:

  var jsId = "00001";
  var worker = new Worker('js/setTime.js');
  worker.postMessage(jsId);

 

         worker线程中:

//接受事件参数
onmessage = function(e) {

    console.log(e.data[0])
}

这样就完成了一个主线程向worker线程传递参数的过程。同样如果worker线程要向主线程传递参数反过来写即可。

 onmessageerror:

在消息传递过程出现错误的属性事件。示例:worker.onmessageerror=function(){....};

方法

postMessage:

向线程worker的内部范围发送消息,可以设置参数,发送给worker线程的数据。在onmessage中接受。

terminate:

过多的开启worker线程非常浪费资源所以在使用过后可以终止它,终止方法使用terminate()。示例:worker.terminate();

close:

除了上面的关闭,如果是在worker线程自身也可以使用self.close()关闭。

 计时器示例

上面说了那么多都是介绍worker的一些基本属性或者方法的使用。下面通过具体的示例来看效果。

我们就拿最常用的倒计时来做示例说明。很简单的一个例子。我们在业务中经常遇到倒计时业务,在倒计时的时候还要做一些其他的业务。因为js单线程的特性,你会发现你的倒计时在你进行其他业务操作的时候是暂停了的。例如现在是9:57你进行了三秒的业务处理。等业务处理完成应该是:9:54,但是你的倒计时还是9:57.就很明显的说明了这一个现象。

场景业务设计

那么我们现在设计这么一个业务操作,

  • 首先我们页面有一个定时器和一个业务操作按钮(用来模拟耗时的操作)。
  • 然后把定时器写到一个worker中进行倒计时操作。
  • 最后通过消息通讯把每次的倒计时时间发送给主线程让主线程修改显示时间。
  • 结束倒计时完毕结束定时器和线程

 有人可能会说为什么还要回到主线程修改时间显示值,请看一下上面的使用规则,我本来也是打算进行主线程传值dom给worker线程奈何不行只能在回传回来。

代码展示

 Html代码:

<body>
        <div>
            <span id="Minute_p">10</span> :
            <span id="Second_p">00</span>
        </div>

        <button type="button" onclick="business()">耗时操作</button>
</body>

主线程js代码:

            //页面加载完成后初始化
            window.onload = function() {
                //创建定时器线程
                var worker = new Worker('js/setTime.js');
                //获取dom对象
                var domMinute_p = document.getElementById('Minute_p');
                var domSecond_p = document.getElementById('Second_p');
                worker.postMessage(600);

                //这里可以接受worker线程的返回值
                worker.onmessage = function(event) {
                    var totalSecond = event.data;
                    console.log(totalSecond)
                    //计算分钟数
                    var minute_p = parseInt(totalSecond / 60);
                    domMinute_p.innerText = minute_p;
                    //计算秒数
                    var second_p = parseInt(totalSecond % 60);
                    domSecond_p.innerText = second_p;
                }
            }
            //这里是模拟的耗时操作
            function business() {
                var data = [1, 2, 3, 4, 5];
                for(var i = 1; i < 1000; i++) {
                    for(var j = 1; j < 1000; j++) {
                        for(var k = 1; k < 5000; k++) {
                            var b = k * 100;
                        }
                    }
                }
                console.log("业务终于走完了!")
            }

worker线程js代码:

var totalSecond = 600;
var domMinute_p, domSecond_p,

    //接受事件参数
    onmessage = function(e) {
        console.log(e.data)
        domMinute_p = e.data;
    }
var timeId = setInterval(function() {
    totalSecond--;

    if(totalSecond == 0) {
        self.close();
    }
    console.log(totalSecond)
    postMessage(totalSecond)

}, 1000)

好了大致示例就是这么多。下面是截图效果:

 开始运行后编号1会开始倒计时,但是当你点击了编号2进行了模拟耗时后,编号1还是会卡住,只有完成编号2后才会运行,但是不同与上面说到的单线程是,他再次运行时的时间是正确时间,还是刚才的例子如果是9:57,点击编号2模拟耗时了3秒,耗时完成后编号1会显示9:54而不是单线程的9:57。就说明worker现在在耗时操作的时候是持续运行的,时间卡只不过是主线程的dom操作被卡住了而已(可以把耗时业务也开启worker就不卡住了)。这里只是介绍worker的使用,所有就不纠结这个界面显示的问题。

补充界面显示方法:

后来有些人就问我怎弄界面显示,我还是真的心疼你们啊,不知道举一反三吗,当然是吧业务耗时也放到后台线程啊,哈哈!!!

再特此说明一个问题,仅在安卓测试:就是定时器在息屏模式下仍继续执行

我还是上面的例子做个例子:

把耗时业务放到business.js文件

onmessage = function(e) {
    console.log(e.data)
    for(var i = 1; i < 1000; i++) {
        for(var j = 1; j < 1000; j++) {
            for(var k = 1; k < 5000; k++) {
                var b = k * 100;
            }
        }
    }
    console.log("耗时业务走完了");
    postMessage(1)
}

然后主文件js调用就好了啊:

                    //这里是模拟的耗时操作
            function business() {
                var worker = new Worker('js/business.js');
                worker.postMessage("开启任务耗时");
                worker.onmessage = function(event) {
                    if(event == 1) {
                        console.log("点击一次完成")
                        worker.terminate()
                    }

                }
            }

截图效果:

 

作者:YanBigFeg —— 颜秉锋

出处:http://www.cnblogs.com/yanbigfeg

本文版权归作者和博客园共有,欢迎转载,转载请标明出处。如果您觉得本篇博文对您有所收获,觉得小弟还算用心,请点击右下角的 [推荐],谢谢!

目录
相关文章
|
3月前
|
数据采集 并行计算 JavaScript
实战指南:在 Node.js 中利用多线程提升性能
在 Node.js 的世界中,多线程技术一直是一个受到广泛关注的领域。最初,Node.js 设计为单线程模式。随着技术发展,Node.js 引入了多线程支持,进而利用多核处理器的强大性能,提升了应用性能。接下来的内容将深入探讨 Node.js 如何实现多线程,以及在何种场合应该采用这种技术。
|
18天前
|
JavaScript 前端开发
JS 单线程还是多线程,如何显示异步操作
JS 单线程还是多线程,如何显示异步操作
21 2
|
3月前
|
消息中间件 JavaScript 前端开发
Node.js 中的线程 与 并发
Node.js 中的线程 与 并发
15 0
|
3月前
|
消息中间件 前端开发 JavaScript
JavaScript 线程:处理高并发任务的必备知识(下)
JavaScript 线程:处理高并发任务的必备知识(下)
JavaScript 线程:处理高并发任务的必备知识(下)
|
3月前
|
前端开发 JavaScript UED
JavaScript 线程:处理高并发任务的必备知识(上)
JavaScript 线程:处理高并发任务的必备知识(上)
JavaScript 线程:处理高并发任务的必备知识(上)
|
4月前
|
前端开发 JavaScript
异步编程:由于JS是单线程执行的,所以对于耗时的操作(如网络请求),需要通过异步编程来处理。回调函数、Promise、async/await都是常用的异步编程方式。
异步编程:由于JS是单线程执行的,所以对于耗时的操作(如网络请求),需要通过异步编程来处理。回调函数、Promise、async/await都是常用的异步编程方式。
41 1
|
9月前
|
JavaScript 前端开发
js单线程、同步、异步
什么是单线程?同步、异步的产生?
83 0
|
12月前
|
JavaScript
Arcgis js多线程克里金插值初体验
最近做关于雨量插值的项目,本来使用后台的GP工具做的,但是处理时间比较长需要十几秒钟左右,所以研究怎么通过前台来计算。
92 0
|
移动开发 JavaScript 前端开发
前端开发面试题—JavaScript执行机制(同步与异步,补充:线程与进程)
今天分享一下我遇到的一个面试题,是关于JavaScript执行机制——同步与异步的问题,解释一下什么是同步和异步呢?
188 0
前端开发面试题—JavaScript执行机制(同步与异步,补充:线程与进程)
|
机器学习/深度学习 JavaScript 前端开发
node.js 中单线程—非阻塞 IO 解释|学习笔记
快速学习 node.js 中单线程—非阻塞 IO 解释
283 0
node.js 中单线程—非阻塞 IO 解释|学习笔记