前言
来啦老铁!
今天咱们暂不学习Spring Boot,如果您对Spring Boot感兴趣,麻烦移步、关注专题:
Spring Boot全家桶近期在搭建公司Web视频编辑器SDK的接口自动化框架过程中,遇到几个新名词,是我之前所不了解的,他们就是:
-
Web Worker;
-
Blob;
笔者自诩对Node.js还算玩的溜,没曾想在前端javascript还是半个文盲,您说咱们是不是得补一补?
整体学习路径
- Web Worker的由来;
- 什么是Web Worker?
- 动手实践Web Worker;
- 什么是Blob?
- 使用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:
- Web Worker:使得js有多线程效果!
- Blob:可以利用Blob动态创建Web Worker!
如果本文对您有帮助,麻烦点赞、关注!
谢谢!
网友评论