You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular V8构建Web Worker时找不到核心Angular对象

解决Angular 8中Web Worker发起HTTP请求的编译错误

首先,咱们得先搞清楚你遇到的问题根源:Web Worker运行在独立的线程中,没有浏览器DOM环境,而且Angular的服务依赖Angular的注入系统,你直接在Worker里导入Angular核心模块、传递服务实例的做法,本身就不符合Worker的运行规则。下面是一步步的解决方案:

核心问题拆解

你看到的编译错误(找不到Element/Node、找不到.ngfactory.ts文件),本质是:

  • Angular的@angular/core和@angular/common/http模块依赖DOM类型定义,但Web Worker环境不包含这些DOM对象,编译时自然报错。
  • 你试图把Angular服务实例传递给Worker,但服务是Angular注入器创建的,包含内部依赖和状态,序列化后会丢失所有功能,根本无法在Worker中正常调用。

解决方案步骤

1. 重构Web Worker:移除Angular依赖,改用原生HTTP请求

Worker里不能用Angular的HttpClient,咱们换成原生fetch或者RxJS的fromFetch来发起请求,完全脱离Angular模块的依赖。

修改你的Worker文件如下(保留核心逻辑,去掉Angular服务导入):

/// <reference lib="webworker" />
import { fromFetch } from 'rxjs/fetch';
import { switchMap, catchError, repeatWhen, delay, tap } from 'rxjs/operators';
import { of } from 'rxjs';
import { SpringBatchJobExecutionStatus } from '../../../../shared/models/SpringBatchJobExecutionStatus';

function sleep(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function postMessageWithLog(message: SpringBatchJobExecutionStatus) {
  console.log('Sending message back: ' + JSON.stringify(message));
  postMessage(message);
}

self.addEventListener('message', async ({ data }) => {
  // 从主线程接收必要参数:文件、API地址、请求头
  const { file, apiUrls, requestHeaders } = data;
  console.log('@@@ Begin addEventListener');

  // 1. 上传文件的逻辑
  const formData = new FormData();
  formData.append('file', file);

  fromFetch(apiUrls.postAsyncFiles, {
    method: 'POST',
    body: formData,
    headers: requestHeaders
  }).pipe(
    // 处理上传响应
    switchMap(response => {
      if (!response.ok) throw new Error('Upload failed');
      return response.json() as Promise<SpringBatchJobExecutionStatus>;
    }),
    tap(postAsyncFilesData => {
      postAsyncFilesData.httpRequestSuccessful = true;
      postMessageWithLog(postAsyncFilesData);
    }),
    // 2. 轮询任务状态的逻辑
    switchMap(postAsyncFilesData => {
      if (postAsyncFilesData.exitStatus === 'RUNNING' || postAsyncFilesData.batchStatus === 'STARTING') {
        return fromFetch(
          `${apiUrls.getAsyncFiles}?jobInstanceId=${postAsyncFilesData.jobInstanceId}&jobExecutionId=${postAsyncFilesData.jobExecutionId}`,
          { headers: requestHeaders }
        ).pipe(
          switchMap(res => res.json() as Promise<SpringBatchJobExecutionStatus>),
          tap(jobExecutionResult => postMessageWithLog(jobExecutionResult)),
          repeatWhen(obs => obs.pipe(delay(10000))), // 每10秒轮询一次
          catchError(error => {
            console.log('Error getting job execution status ' + error);
            const errorBlock = new SpringBatchJobExecutionStatus();
            errorBlock.httpRequestSuccessful = false;
            postMessageWithLog(errorBlock);
            return of(errorBlock); // 终止轮询
          })
        );
      } else {
        const errorBlock = { ...postAsyncFilesData, httpRequestSuccessful: false };
        console.log(`Not entering check loop: batchStatus=${errorBlock.batchStatus}, exitStatus=${errorBlock.exitStatus}`);
        postMessageWithLog(errorBlock);
        return of(errorBlock);
      }
    }),
    // 处理上传失败的情况
    catchError(error => {
      const errorBlock = new SpringBatchJobExecutionStatus();
      errorBlock.httpRequestSuccessful = false;
      postMessageWithLog(errorBlock);
      return of(errorBlock);
    })
  ).subscribe();

  console.log('@@@ addEventListener(...): worker ended');
});

2. 主线程传递参数(而非服务实例)

在你的Angular组件中,创建Worker时传递Worker需要的所有数据(文件、API地址、请求头等),不要传递服务实例:

import { Component } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

@Component({
  selector: 'app-upload',
  templateUrl: './upload.component.html'
})
export class UploadComponent {
  constructor(private http: HttpClient) {}

  onFileSelected(files: FileList) {
    if (!files.length) return;

    // 构造请求头(比如添加认证token等)
    const headers = new HttpHeaders();
    // 示例:headers.set('Authorization', `Bearer ${yourAuthToken}`);

    // 创建Worker
    const worker = new Worker('./softcopy-report-file-uploader.worker', { type: 'module' });

    // 传递参数给Worker
    worker.postMessage({
      file: files.item(0),
      apiUrls: {
        postAsyncFiles: '/api/softlayer/postAsyncFiles', // 替换为你的实际API地址
        getAsyncFiles: '/api/softlayer/getAsyncFiles'
      },
      requestHeaders: headers.keys().reduce((acc, key) => {
        acc[key] = headers.get(key);
        return acc;
      }, {} as Record<string, string>)
    });

    // 监听Worker返回的消息
    worker.onmessage = ({ data }) => {
      console.log('Received from worker:', data);
      // 在这里更新UI状态,比如进度、结果提示等
    };

    // 监听Worker错误
    worker.onerror = (error) => {
      console.error('Worker error:', error);
      worker.terminate();
    };
  }
}

3. 配置Worker的TypeScript编译规则

创建/修改tsconfig.worker.json文件,确保Worker编译时只使用Web Worker所需的类型库,排除DOM相关依赖:

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./out-tsc/worker",
    "lib": ["es2018", "webworker"],
    "types": []
  },
  "include": [
    "src/**/*.worker.ts"
  ]
}

然后修改angular.json的构建配置,指定Worker使用这个tsconfig:

"architect": {
  "build": {
    "builder": "@angular-devkit/build-angular:browser",
    "options": {
      // 其他配置...
      "workerTsConfig": "tsconfig.worker.json"
    }
  }
}

为什么原来的方法行不通?

  1. Web Worker无DOM环境:Angular核心模块依赖DOM类型(比如Element、Node),但Worker线程没有这些对象,编译时必然报错找不到类型定义。
  2. Angular服务无法跨线程传递:Angular服务是通过注入器创建的,包含内部依赖和运行时状态,序列化后传递给Worker会变成普通对象,所有方法都会失效。

内容的提问来源于stack exchange,提问作者Woodsman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 07:02:48