在web worker中使用fetch实例详解

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

在web worker中使用fetch实例详解

Youky   2022-11-16 我要评论

1.Web Worker意义

由于 JS 是单线程的,费时的 JS 操作将会导致整个页面的阻塞。Web Worker 提供了创建多线程的方法,将一些耗时且 UI 无关的工作交给 worker,可提高页面的使用体验。

限制:

同源策略:worker 线程执行的脚本要和当前页面同源

API 限制:

  • 不能操作 DOM
  • 不能使用 window 的全局变量,但可以使用 navigator 和 location 对象
  • 不能使用 alert、confirm 方法
  • 无法读取本地文件

和主线程不在一个上下文环境,通讯要通过 postMessage 完成

2. 主线程的使用

创建

创建一个子线程,要传入一个脚本的 URL。如果该脚本加载失败,则 Worker 会静默失败

const worker = new Worker('url');

如果要在本文件中描述执行的内容,可以用 Blob 和 window.URL.createObjectURL 生成一个 URL

function createWorker(f) {
  const blob = new Blob(['(' + f.toString() +')()']);
  const url = window.URL.createObjectURL(blob);
  const worker = new Worker(url);
  return worker;
}

通信

  • 主线程 => 子线程
worker.postMessage(param);

参数可以是任意类型,包括二进制数据。但传递是拷贝形式而不是引用形式。因此对于大数据会存在性能问题。

  • 子线程 => 主线程
worker.onmessage = function (event) {
  console.log('Received message ' + event.data);
}

错误处理

worker.onerror(function (e) {
  console.log([
    'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message
  ].join(''));
});

关闭

worker.terminate();

3. 子线程的使用

子线程中无法使用 window,self 代表全局对象

和主线程的通信

  • 主线程 => 子线程
self.addEventListener('message', function (e) {
  self.postMessage('Received: ' + e.data);
}, false);
  • 子线程 => 主线程
self.postMessage('something');

加载其他脚本

在子线程中加载其他脚本:

importScripts('script1.js', 'script2.js');

关闭

self.close();

4. 在WebWorker中使用fetch

网络请求是和DOM无关且可能耗时较长的操作,worker线程支持使用Fetch,是适合放在worker中进行的操作。

而要在worker中使用fetch,如果每次都要自己处理线程间的通信的话,会十分麻烦,因此我对通信进行了封装,写成了一个可以直接使用的库

安装依赖:

npm i web-worker-fetch

使用时先实例化一个WF对象,然后就可以像使用fetch一样在worker中使用fetch:

import WebWorkerFetch from "web-worker-fetch";
const wf = new WebWorkerFetch();
wf.fetch("url", {
  method: "POST", // *GET, POST, PUT, DELETE, etc.
  mode: "cors", // no-cors, *cors, same-origin
  cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
  credentials: "same-origin", // include, *same-origin, omit
  headers: {
    "Content-Type": "application/json"
    // 'Content-Type': 'application/x-www-form-urlencoded',
  },
  redirect: "follow", // manual, *follow, error
  referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
  body: JSON.stringify(data) // body data type must match "Content-Type" header
}).then((res) => console.log(res));

除此之外,借鉴 axios 的思路,配置中可以提供 requestInterceptorresponseInterceptor,对请求参数和返回数据做统一处理

5. 实现思路

这个库的封装主要是解决了两个问题:

  • 发送请求时如何把参数传递给worker线程
  • 请求结束后如何从worker线程获取结果

对于第一个问题,主线程使用 ostMessage 向worker线程传递参数。

对于第二个问题,worker线程通过 self.postMessage 向主线程传递消息,主线程通过 worker.onmessage 监听消息。

此时就引出了问题所在:如果多次使用 wf.fetch 发送请求,那么在一个请求完成后,worker线程触发的消息将让所有请求处都认为请求已完成。

因此,在每次请求时,使用一个fetchId确定该请求做唯一性。将该id传给worker线程,后续worker线程向主线程通信时也会带上这个id。

在主线程中监听onmessage事件时,判断id是否和自己的请求id一致,只有在相同时才做处理。

具体的实现大家可以移步仓库源码,实际上也非常简单。

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们