美文网首页JavaScript 极简教程 ES6 ES7
你知道JavaScript中的Web Worker和Blob吗?

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

作者: 狄仁杰666 | 来源:发表于2020-12-14 00:13 被阅读0次

    前言

    来啦老铁!

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

    Spring Boot全家桶

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

    • Web Worker;

    • Blob;

    笔者自诩对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窗口:
    关闭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:

    • Web Worker:使得js有多线程效果!
    • Blob:可以利用Blob动态创建Web Worker!

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

    谢谢!

    相关文章

      网友评论

        本文标题:你知道JavaScript中的Web Worker和Blob吗?

        本文链接:https://www.haomeiwen.com/subject/vpjziktx.html