chenshenhai / blog

个人博客,没事写写玩玩~~~
146 stars 21 forks source link

浏览器在同js文件下执行后台线程Worker #35

Open chenshenhai opened 5 years ago

chenshenhai commented 5 years ago

前言

对于js的Worker的使用,看了很多网上资料,都是基于worker.js新开一个文件来执行后台线程。

但是如果不想太麻烦新开一个work.js文件,想在同一个js文件里执行后台线程的资料就很少,最后翻遍了MDN 文档,找到了一种在同js文件下执行后台线程Worker的方法,具体实现如下

实现

实现源码

/**
 * 执行函数的后台线程
 * @param func 待后台线程执行的函数
 * @param params 待后台线程执行的函数参数
 * @param feedback 执行后台线程函数后反馈回调函数
 */
 const asyncWorker = function (func, params, feedback) {
   // 设置后台执行函数的UUID
  const uuid = Math.random().toString(26).substr(2);

  // 封装成自执行函数字符串
  const scriptCode = `(${func.toString()})(event.data.params)`;
  const feedbackMap = new Map();
  // 后台线程代码字符串
  const workerCode = `
    onmessage = function (event) {
      let result = null;
      let err = null;
      result = eval(event.data.code);
      postMessage({
        id: event.data.id,
        result: result,
        error: err,
      });
    }
  `;
  const workerCodeStr = encodeURIComponent(workerCode);
  // 初始化后台线程
  const worker = new Worker('data:text/javascript;charset=US-ASCII,' + workerCodeStr);

  // 监听正常后台线程通信
  worker.onmessage = function (event) {
    const callback = feedbackMap.get(event.data.id);
    if (typeof callback === 'function') {
      callback(event.data.result, event.data.error);
    }
    feedbackMap.delete(event.data.id);
  };

  // 捕获后台线程错误
  worker.onerror = function (err) {
    const callback = feedbackMap.get(uuid);
    if (typeof callback === 'function') {
      callback(null, err.message);
    }
    feedbackMap.delete(uuid);
  };

  // 将函数存入map中
  feedbackMap.set(uuid, feedback);

  // 发起通信,执行ID为UUID的函数
  worker.postMessage({
    id: uuid,
    params: params,
    code: scriptCode
  });
};

执行正常后台线程

// 待执行在后台线程函数
// 斐波那契数列函数
const fibonacciFunc = function(params = {}) {
  const { count = 1 } = params;
  let result = 1;
  for (let i = 0; i < count; i ++) {
    result += result;
  }
  return result;
}
// 斐波那契数列的参数为 50
const params = { count: 50 };
// 开始执行后台线程 计算数列
asyncWorker(fibonacciFunc, params, function (result, err) {
  console.log('result = ', result);
  console.log('error = ', err);
});
执行结果
# 斐波那契数列 执行结果
result =  1125899906842624
error =  null

执行异常后台线程

// 待执行在后台线程 异常函数
const errorFunc = function(params = {}) {
  throw new Error('i am an error')
}
// 开始执行后台线程 异常函数
asyncWorker(errorFunc, {}, function (result, err) {
  console.log('result = ', result);
  console.log('error = ', err);
});
执行结果
result =  null
error =  Uncaught Error: i am an error

参考

MDN:Web/API/Web_Workers_API/Using_web_workers