web worker - 传递回主线程时数据未定义

web worker - data undefined when passed back to main thread

我正在尝试使用网络工作者获取一些数据,然后将它们传回主线程。我已尝试使用此代码,但它无法按预期工作

onmessage = (e) => {
  console.log(e);
  if( e.data[0] === 'fetchData' ){
    fetch('https://example.com/platform/api/v1/endpoint')
    .then( (res) => res.json() )
    .then( async (data) => {
      const imagesData = await Promise.all(
        data.map( async (item) => {
          let res = await fetch(item.src);
          let img = await res.blob();
          let reader = new FileReader();
          reader.readAsDataURL(img);
          reader.onloadend = () => { 
            return { 
              title: item.title, 
              link: item.link, 
              src: reader.result 
            }
          }
        })
      )
      postMessage(imagesData);
    })
  }
}

console.log 之后的 imagesData 将包含一个包含七个未定义元素的数组,我该如何解决这个问题?

更新

我已经将 vue 前端中的代码更改到 worker 中,现在我能够获取数据,但有时 worker 无法工作,我将无法获取数据,或者我只能获取两个条目,但前端的预期数量是七个项目。这是我修改代码的方式,也许我需要在使用另一个之前终止工作? 注意:我正在创建一个选项卡覆盖 chrome 扩展

vue前端代码

<script>
const worker = new Worker('services.js');

export default {
  name: 'App',
  beforeCreate() {
    worker.postMessage(['fetchData']);
  },
  created() {
    this.init();
    this.clock();
  },
  data() {
    return {
      mostVisited: [],
      imagesData: [],
      isLoading: true 
    }
  },
  methods: {
    init() {
      worker.onmessage = (e) => {
        console.log(e);
        this.imagesData = e.data; 
        this.isLoading = false; 
      }
      browser.topSites.get().then( (sites) => this.mostVisited = sites ); 
    } //end init
  }
</script>

网络工作者代码

onmessage = (e) => {
  console.log(e);
  if( e.data[0] === 'fetchData' ){
    fetch('https://example.com/platform/api/v1/endpoint')
    .then( (res) => res.json() )
    .then( async (data) => {
      let imagesData = [];
      await Promise.all(
        data.map( async (item) => {
          let res = await fetch(item.src);
          let img = await res.blob();
          let reader = new FileReader();
          reader.readAsDataURL(img);
          reader.onloadend = () => { 
            imagesData.push({ title: item.title, link: item.link, src: reader.result });
          }
        })
      )
      postMessage(imagesData);
    }); // end then(data)
  }
}

在解析外部 Promise 之前,您无需等待异步 FileReader 读取您的文件,因此 Promise.all Promise 在 let img = await res.blob(); 之后但在 onloadend 之前解析。

因为你在 Worker 上下文中,你可以使用 synchronous FileReaderSync API,这会给出类似

.then( async (data) => {
  let imagesData = [];
  await Promise.all(
    data.map( async (item) => {
      let res = await fetch(item.src);
      let img = await res.blob();
      let reader = new FileReaderSync();
      const result = reader.readAsDataURL(img);
      imagesData.push({ title: item.title, link: item.link, src: result });
    })
  )
  postMessage(imagesData);
});

但我有 99% 的信心您甚至不需要这些数据:URL,而且它的危害比什么都大。

请记住,来自 FileReader 的数据:URL 始终编码为 base64,这将生成包含原始数据 134% 的 DOMString,由于 DOMString 以 UTF-16 格式存储,因此每 2 个乘以一次。
然后,为了将该数据传递给主上下文,浏览器将必须使用 structured clone algorithm 序列化数据,对于 DOMStrings 这意味着在内存中进行简单复制。现在每个图像占用的内存大约是其实际大小的 5 倍,甚至在主上下文开始再次将其解析为二进制数据以便构建像素数据之前...

相反,只需将您获得的 Blob 传递为 img
,所以你复制的只是周围的小 js 包装器。
然后当你需要在主上下文中显示这些图像时,使用 URL.createObejctURL(img) 这将 return 一个 blob: URL 直接指向同一个 Blob 的数据,它仍然只在内存中存储一​​次.

.then( async (data) => {
  let imagesData = [];
  await Promise.all(
    data.map( async (item) => {
      let res = await fetch(item.src);
      let img = await res.blob();
      const url = URL.createObjectURL(img);
      imagesData.push({ title: item.title, link: item.link, src: url, file: img });
    })
  )
  postMessage(imagesData);
});