关于前端:Web-Woker-常见使用问题和解决方案

54次阅读

共计 5834 个字符,预计需要花费 15 分钟才能阅读完成。

问题汇总

  1. new Worker(aURL, options) URL 必须恪守同源策略。同源策略是浏览器的一种平安个性,限度了在不同源(协定、域名、端口)之间的 JavaScript 代码的拜访。这意味着在 Web Worker 中,只能加载与以后页面在同一源下的脚本,否则会触发平安谬误。
  2. Web Worker 中限度了局部 Web API。Web Worker 有一些限度,其中包含无奈间接操作 DOM 和无奈应用 localStorage。这是因为 Web Workers 是在独立的线程中运行的,与主线程拆散,并且没有间接的拜访主线程的 DOM 或 JavaScript 运行环境的能力。
  3. Web Worker 与主线程的数据通信,默认状况下 Web Worker 与主线程或其余 Worker 不能共享内存。Web Workers 默认状况下是无奈间接共享内存的,因为它们在独立的线程中运行,领有各自的运行环境和内存空间。

解决方案

利用 Blob 解决跨域限度

Web Workers 是一种在 JavaScript 中创立并在后盾运行的多线程形式,能够用于执行耗时的工作而不会阻塞主线程。然而在应用 Web Workers 时,须要留神一些限度和解决方案,其中包含同源策略。这意味着在 Web Worker 中,只能加载与以后页面在同一源下的脚本,否则会触发平安谬误。

// 获取近程脚本代码
fetch('https://example.com/remote-script.js')
  .then(response => response.text())
  .then(scriptText => {
    // 创立 Blob 对象
    const blob = new Blob([scriptText], {type: 'application/javascript'});

    // 创立 Blob URL
    const blobUrl = URL.createObjectURL(blob);

    // 创立 Web Worker
    const worker = new Worker(blobUrl);

    // Web Worker 的音讯解决逻辑
    worker.onmessage = function(event) {
      // 解决 Web Worker 发送的音讯
      console.log('Message from Web Worker:', event.data);
    };

    // 向 Web Worker 发送音讯
    worker.postMessage('Hello from main thread!');
  })
  .catch(error => {
    // 处理错误
    console.error('Failed to load remote script:', error);
  });

在这个示例中,通过应用 fetch() 办法获取近程脚本代码,并将其作为 Blob 对象传递给 Worker 构造函数。而后,通过 Blob URL 创立一个 Web Worker,并在 Web Worker 中解决音讯。因为 Blob URL 不受同源策略限度,因而能够加载近程的脚本代码。

须要留神的是,加载近程代码可能存在平安危险,因而在应用 Blob 对象加载近程代码时应审慎,并确保加载的代码是可信的。此外,因为 Blob URL 是长期的,当不再须要时应应用 URL.revokeObjectURL() 办法来开释资源,以防止内存透露。

解决 API 限度

通过利用主线程对 localStorage 和 DOM 等 API 进行代理。Web Workers 能够通过 postMessage() 办法向主线程发送音讯,主线程能够通过监听 message 事件来接管音讯。通过这种形式,Web Worker 能够向主线程申请 DOM 操作或 localStorage 操作,并将后果作为音讯发送回来。

Web Workers 能够通过 postMessage() 办法向主线程发送音讯,主线程能够通过监听 message 事件来接管音讯。通过这种形式,Web Worker 能够向主线程申请 DOM 操作或 localStorage 操作,并将后果作为音讯发送回来。

// 发送申请获取 localStorage 中的数据
self.postMessage({type: 'getLocalStorage', key: 'myKey'});
// 监听 Web Worker 发送的音讯
worker.onmessage = function(event) {
  // 依据音讯类型执行相应的操作
  if (event.data.type === 'getLocalStorage') {
    // 从 localStorage 中获取数据
    const value = localStorage.getItem(event.data.key);

    // 发送后果回 Web Worker
    worker.postMessage({type: 'localStorageData', value});
  }
};

解决数据通信问题

Web Workers 默认状况下是无奈间接共享内存的,因为它们在独立的线程中运行,领有各自的运行环境和内存空间。

利用 postMessage 传递

Web Workers 能够通过 postMessage() 办法向其余 Worker 或主线程发送音讯,并通过监听 message 事件来接管音讯。通过这种形式,能够在不同的 Workers 或主线程之间传递数据和进行通信。

// 创立两个 Web Workers
const worker1 = new Worker('worker1.js');
const worker2 = new Worker('worker2.js');

// 向 worker1 发送音讯
worker1.postMessage('Hello from main thread to worker1!');

// 监听 worker1 发送的音讯
worker1.onmessage = function(event) {console.log('Message from worker1:', event.data);
};

// 向 worker2 发送音讯
worker2.postMessage('Hello from main thread to worker2!');

// 监听 worker2 发送的音讯
worker2.onmessage = function(event) {console.log('Message from worker2:', event.data);
};

在 worker1.js 中监听音讯并向主线程回复:

// 监听主线程发送的音讯
self.onmessage = function(event) {console.log('Message from main thread to worker1:', event.data);

  // 向主线程回复音讯
  self.postMessage('Hello from worker1!');
};

在 worker2.js 中监听音讯并向 worker1 发送音讯:

// 监听主线程发送的音讯
self.onmessage = function(event) {console.log('Message from main thread to worker2:', event.data);

  // 向 worker1 发送音讯
  self.postMessage('Hello from worker2 to worker1!');
};

那么如果 worker1.js 想要和 worker2.js(兄弟 Worker)进行通信呢?只应用postMessage,我想到的比拟麻烦一点,就是通过主线程进行音讯代理,例如以下办法进行革新:

// 创立两个 Web Workers
const worker1 = new Worker('worker1.js');
const worker2 = new Worker('worker2.js');

// 向 worker1 发送音讯
worker1.postMessage('Hello from main thread to worker1!');

// 监听 worker1 发送的音讯
worker1.onmessage = function(event) {console.log('Message from worker1:', event.data);

  // 如果 worker1 发送了音讯给 worker2
  if (event.data.to === 'worker2') {
    // 将音讯转发给 worker2
    worker2.postMessage(event.data.message);
  }
};

// 向 worker2 发送音讯
worker2.postMessage('Hello from main thread to worker2!');

// 监听 worker2 发送的音讯
worker2.onmessage = function(event) {console.log('Message from worker2:', event.data);

  // 如果 worker2 发送了音讯给 worker1
  if (event.data.to === 'worker1') {
    // 将音讯转发给 worker1
    worker1.postMessage(event.data.message);
  }
};

在 worker1.js 中,如果想要向 worker2 发送音讯,能够通过将音讯发送给主线程,并指定接收者为 worker2:

// 向主线程发送音讯,并指定接收者为 worker2
self.postMessage({to: 'worker2', message: 'Hello from worker1 to worker2!'});

在 worker2.js 中,如果想要向 worker1 发送音讯,也能够通过将音讯发送给主线程,并指定接收者为 worker1:

// 向主线程发送音讯,并指定接收者为 worker1
self.postMessage({to: 'worker1', message: 'Hello from worker2 to worker1!'});

这样就能够实现两个 Web Workers 之间的通信了。须要留神的是,因为主线程作为音讯代理,可能会造成肯定的性能开销,因而应该依据理论状况审慎应用,更举荐以下应用的共享内存办法。

共享内存

Web Workers 还提供了 SharedArrayBufferAtomics API,容许多个 Workers 共享同一块内存,从而实现更高效的数据共享和通信。

// index.js
const sharedBuffer = new SharedArrayBuffer(4); // 创立一个共享内存,蕴含 4 个字节
const worker1 = new Worker('worker1.js');
const worker2 = new Worker('worker2.js');

// 向 worker1 和 worker2 传递共享内存
worker1.postMessage({type: 'buffer', buffer: sharedBuffer});
worker2.postMessage({type: 'buffer', buffer: sharedBuffer});

接管共享内存,并应用 Atomics API 进行原子操作:

// worker1
self.onmessage = (event) => {
  const data = event.data;
  if (data.type === 'buffer') {const sharedArray = new Int32Array(data.buffer); // 创立一个 Int32Array 视图来拜访共享内存

    // 在共享内存中进行原子加法操作
    Atomics.add(sharedArray, 0, 1); // 将共享内存中索引为 0 的值减少 1

    // 向 worker2 发送一个音讯,蕴含共享内存中索引为 0 的值
    self.postMessage({type: 'value', value: sharedArray[0] });
  }
};
// worker2
self.onmessage = (event) => {
  const data = event.data;
  if (data.type === 'buffer') {const sharedArray = new Int32Array(data.buffer); // 创立一个 Int32Array 视图来拜访共享内存

    // 在共享内存中进行原子加法操作
    Atomics.add(sharedArray, 0, 2); // 将共享内存中索引为 0 的值减少 2

    // 向主线程发送一个音讯,蕴含共享内存中索引为 0 的值
    self.postMessage({type: 'value', value: sharedArray[0] });
  }
};

在主线程中,别离接管来自 worker1 和 worker2 的音讯,并输入共享内存中的值:

// index.js
// 接管来自 worker1 的音讯,并输入共享内存中的值
worker1.onmessage = (event) => {
  const data = event.data;
  if (data.type === 'value') {console.log('worker1 value:', data.value); // 输入共享内存中索引为 0 的值
  }
};

// 接管来自 worker2 的音讯,并输入共享内存中的值
worker2.onmessage = (event) => {
  const data = event.data;
  if (data.type === 'value') {console.log('worker2 value:', data.value); // 输入共享内存中索引为 0 的值
  }
};

SharedArrayBuffer 是一种非凡的 ArrayBuffer,它能够在多个 Workers 之间共享,容许它们在同一块内存中进行读写操作。与一般的 ArrayBuffer 不同,SharedArrayBuffer 不受同源策略限度,因而能够在不同源的 Workers 之间进行共享。

Atomics API 则提供了一组原子操作,用于在共享的内存中进行同步和协调拜访。它包含了一些常见的原子操作,例如原子加法、原子减法、原子比拟和替换等,能够保障多个 Workers 在拜访共享内存时的原子性操作,防止了数据竞争和不一致性的问题。

通过应用 SharedArrayBufferAtomics API,多个 Workers 能够在共享的内存中进行高效的数据操作,从而实现更疾速和高效的数据共享和通信,尤其对于大规模数据处理或简单计算的场景下,能够显著晋升性能。然而,须要留神的是,因为共享内存可能波及到并发拜访和竞态条件,应用 SharedArrayBufferAtomics API 须要审慎解决,并遵循相干的安全性和最佳实际,以确保数据的正确性和一致性。

正文完
 0