JavaScript 极简教程 ES6 ES7

你知道JavaScript中的Web Worker和Blob吗?

2020-12-14  本文已影响0人  狄仁杰666

前言

来啦老铁!

今天咱们暂不学习Spring Boot,如果您对Spring Boot感兴趣,麻烦移步、关注专题:

Spring Boot全家桶

近期在搭建公司Web视频编辑器SDK的接口自动化框架过程中,遇到几个新名词,是我之前所不了解的,他们就是:

笔者自诩对Node.js还算玩的溜,没曾想在前端javascript还是半个文盲,您说咱们是不是得补一补?

整体学习路径

  1. Web Worker的由来;
  2. 什么是Web Worker?
  3. 动手实践Web Worker;
  4. 什么是Blob?
  5. 使用Blob动态创建Web Worker;

1. Web Worker的由来;

为了解释Web Worker的由来,我们先一起来学习一下js单线程、异步的原理。简单写了点js代码并简单作了一下图,用于解释js单线程、异步的原理:

js单线程、异步原理图

一起来理解一下:

js是单线程的、异步的?

众所周知,js却是是单线程的,也的确是异步的,很多人都深信不疑,但是我告诉您,这句话是错的,至少不严谨!怎么说?原因是:单线程与异步本身是矛盾的,单线程一定是同步的,即代码是一行一行执行的!

可是js的确是异步的,这又怎么解释?

js本身的确是单线程、同步的,但浏览器不是呀,浏览器是多线程的,浏览器为js提供了任务队列线程。

js主线程一行一行地执行代码,当遇到耗时的代码,就把该任务丢到浏览器提供的任务队列里头,然后js主线程继续往下执行代码;

当js主线程执行完不阻塞的代码后,再到浏览器任务队列里头循环查找那些耗时的任务执行情况,当耗时的任务执行完毕后,js就能拿到耗时任务的结果,也即js主线程才真正完成使命!

因此,在上述“js单线程、异步原理图”中,实际的执行应该是像蓝色的箭头所示。当js主线程执行demo()方法时,先打印出数字1,接着打印数字3,最后才打印出数字2,因为setTimeout是阻塞的(事实上,即使setTimeout等待的时间为0,数字2也是最后打印的),如图:

异步效果
js主线程+浏览器的任务队列线程,才使js达到了异步的效果!nodejs也是类似的!

可是,这跟Web Worker有啥关系呀?

js单线程、异步原理图

仔细看下“js单线程、异步原理图”,浏览器队列只有一个,而且既然是队列,就需要排队,很多业务场景下如计算密集型或高延迟任务中还是不够用的,这时候就衍生出Web Worker!

2. 什么是Web Worker?

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

引入Web Worker后就变成这样了:
Web Worker

每个Web Worker单独占用一个线程(即多线程),Web Worker线程与Web Worker线程间是互不干扰的!

想象一下,在未引入Web Worker的时候,假设任务7是计算密集型或高延迟的任务,而任务8是某UI交互,则UI交互就会被计算密集型或高延迟的任务所阻塞,页面很可能就是卡住、无响应的状态!

而引入Web Worker之后,计算密集型或高延迟的任务被安排到了新的Web Worker线程,并不占用浏览器的任务队列,即像任务8就不会被阻塞,页面就能很流畅!

需要注意的是:由于每个Web Worker均占用新的线程,而线程就会占用系统资源,因此,Web 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://),它所加载的脚本,必须来自网络。

3. 动手实践Web Worker;

1. 编写html页面,如:index.html;
<!DOCTYPE html>
<html>
<head>
    <title>Web Worker实践</title>
    <meta charset="utf-8">
</head>
<body>

<p>计数: <output id="result"></output></p>
<button onclick="startWorker()">开始计数</button> 
<button onclick="stopWorker()">停止计数</button>

<button onclick="alert(123)">Alert操作</button>

</body>
</html>

<script>
    let worker;
    
    function startWorker() {
        worker = new Worker('./index.js');
        worker.onmessage = function(event) {
            document.getElementById('result').innerHTML = event.data;
        }
    }
    
    function stopWorker() { 
        worker.terminate();
    }
</script>
2. 编写要worker的任务,如:index.js;
let i= 0;

function count() {
    i += 1;
    postMessage(i);
    setTimeout(count(), 1000);
}

count();
3. 搭建一个简单服务器;

根据Web Worker的文件限制,我们需要搭建一个简单服务器,来运行我们的例子。服务器搭建可参考:

直接复制其代码,织入新建的main.js文件即可:

var http = require('http');
var fs = require('fs');
var url = require('url');
 
// 创建服务器
http.createServer( function (request, response) {  
   // 解析请求,包括文件名
   var pathname = url.parse(request.url).pathname;
   
   // 输出请求的文件名
   console.log("Request for " + pathname + " received.");
   
   // 从文件系统中读取请求的文件内容
   fs.readFile(pathname.substr(1), function (err, data) {
      if (err) {
         console.log(err);
         // HTTP 状态码: 404 : NOT FOUND
         // Content Type: text/html
         response.writeHead(404, {'Content-Type': 'text/html'});
      }else{             
         // HTTP 状态码: 200 : OK
         // Content Type: text/html
         response.writeHead(200, {'Content-Type': 'text/html'});    
         
         // 响应文件内容
         response.write(data.toString());        
      }
      //  发送响应数据
      response.end();
   });   
}).listen(8080);
 
// 控制台会输出以下信息
console.log('Server running at http://127.0.0.1:8080/');

index.html、index.js、main.js在同一个文件目录下即可!

4. Web Worker演示;

1). 启动项目:

node main.js
启动项目

2). 访问html页面;

访问html页面

3). 验证Web Worker工作情况;

开始计数 点击Alert操作按钮并等待几秒 关闭Alert窗口 停止计数

我们会发现,当alert窗口弹出时,Worker在后台还一直执行,并不会阻碍浏览器的其他事件!停止计数后,再次开始计数也是从1开始数。

通过这个简单的例子,我们就能感受到Web Worker的效果,当计数Worker为其他计算密集型或高延迟的任务时,效果应该会更加明显!

4. 什么是Blob?

对于什么是Blob,读者可自行查阅资料,我们这里只针对Web Worker来介绍Blob。

上面的例子中,我们将模拟的计算密集型或高延迟的任务写在index.js文件中,
但是如果在实际生产中,我们只能运行一个事先写好的js文件,那这worker的局限性就太大了,太不灵活了,事实上,我们往往需要一个动态创建Web Worker的能力,通常我们利用Blob以及URL.createObjectURL()来提供动态创建Web Worker的能力!

5. 使用Blob动态创建Web Worker;

要使用Blob动态创建Web Worker,简单的说就是:

let worker = new Worker('./index.js');

要变成:

let blob = new Blob(XXX)
let worker = new Worker(URL.createObjectURL(blob));

XXX指的是要动态执行的任务!

动手撸一个示例:
将index.html进行改造:

<!DOCTYPE html>
<html>
<head>
    <title>Web Worker实践</title>
    <meta charset="utf-8">
</head>
<body>

<p>计数: <output id="result"></output></p>
<button onclick="startWorker()">开始计数</button> 
<button onclick="stopWorker()">停止计数</button>

<button onclick="alert(123)">Alert操作</button>

</body>
</html>

<script>
    function createBlob() {
        function count() {
            i+=1;
            postMessage(i);
            setTimeout("count()", 1000);
        }
        let blob = new Blob(["let i= 0;" + count.toString() + ' count()'], {type: 'text/javascript'});
        return URL.createObjectURL(blob);
    }
    

    let worker;
    function startWorker() {
        worker = new Worker(createBlob());
        worker.onmessage = function(event) {
            document.getElementById('result').innerHTML = event.data;
        }
    }
    
    function stopWorker() { 
        worker.terminate();
    }
</script>

简单的说URL.createObjectURL();就是把代码与url做个映射关联,url的返回就是Blob对象包装的任务代码!

运行后跟之前是一样的效果哟!
利用Blob动态创建Worker

这样,我们就摆脱了需要事先写好js文件的束缚,能够动态地创建Web Worker喽!

综上,我们用一句话概括Web Worker和Blob:

如果本文对您有帮助,麻烦点赞、关注!

谢谢!

上一篇下一篇

猜你喜欢

热点阅读