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

前言

来啦老铁!

今天咱们暂不学习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!

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

谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341