Angular 4 - 如何在处理下一个请求之前等待Web worker的响应

时间:2017-10-25 15:49:30

标签: angular web-worker

我有一个Angular 4服务,它使用Web worker为缓冲区中排队的文件块发送http请求。如果Web工作者以http错误状态代码响应,则Web工作者不应处理进一步的请求。以下链接说明了我想要实现的目标....

WebWorker for Http requests

我已经实现了一种方法,该方法使用Observables将响应有效负载通知给订户。源代码包含在本文末尾。

当我运行代码时,会报告以下输出(N.B为了快速,我设置Web工​​作人员在收到http响应状态200时强制执行错误响应)。

creating message for chunk 0
Posting actual message with id 0
creating message for chunk 1
Posting actual message with id 1
creating message for chunk 2
Posting actual message with id 2
creating message for chunk 3
Posting actual message with id 3
creating message for chunk 4
Posting actual message with id 4
Completed processing message
Manager create document completed
Web worker sending chunk 0/5
Web worker sending chunk 1/5
Web worker sending chunk 2/5
Web worker sending chunk 3/5
Web worker sending chunk 4/5
Application error received from worker in main thread. Notifying subscriber
Completed observing worker response, closing worker
Upload error {"status":200,"message":"OK"}
Application error received from worker in main thread. Notifying subscriber
Completed observing worker response, closing worker
Upload error {"status":200,"message":"OK"}
Application error received from worker in main thread. Notifying subscriber
Completed observing worker response, closing worker
Upload error {"status":200,"message":"OK"}
Application error received from worker in main thread. Notifying subscriber
Completed observing worker response, closing worker
Upload error {"status":200,"message":"OK"}
Application error received from worker in main thread. Notifying subscriber
Completed observing worker response, closing worker
Upload error {"status":200,"message":"OK"} 

在上面的输出中,所有http请求都是从Web worker发送的。随后,主线程接收响应的通知。

如何确保Web工作者请求和响应同步发生,即请求 - >等待回应 - >请求 - >等待回应?

谢谢你的考虑。

WebWorker

/**
 * This is the main body of the worker process
 * It receives a blob slice for upload and sends post to upload using
 * multipart/form
 * When completed it sends reponse message back to parent incorporating status
 * and response text of the url state
 */
onmessage = (message: MessageEvent) => {

  let type = message.data.type;
  let id = message.data.id;
  let payload = message.data.payload;

  switch (type) {
    case 'UPLOAD': {
      let xhr : XMLHttpRequest = new XMLHttpRequest();
      xhr.onreadystatechange = function () {
        if(xhr.readyState == XMLHttpRequest.DONE) {
          let response = {type: type, id: id, payload: {status: xhr.status, message: xhr.responseText}, undefined};
          if (xhr.status == 200) {
            response.type = 'error';
          }
          self.postMessage(response, undefined);
        }
      };

      xhr.open('POST', payload.url, true /*async must be true when using multipart*/);

      let formData: FormData = new FormData ();
      formData.append ('chunk', payload.chunk);
      formData.append ('chunkid', payload.chunkid.toString());
      formData.append ('documenttypeid', payload.documenttypeid.toString());
      formData.append ('totalchunks', payload.totalchunks.toString());
      formData.append ('taskid', payload.ticketid.toString());

      console.log ('Web worker sending chunk %d/%d', payload.chunkid, payload.totalchunks);
      xhr.send(formData);

    }
  }
};

WebWorkerClient Helper类

import { Headers }                              from '@angular/http';
import { Http }                                 from '@angular/http';

import { Injectable }                           from '@angular/core';
import { NgZone }                               from '@angular/core';
import { Observable }                           from 'rxjs/Rx';

import { Request }                              from '@angular/http';
import { ReplaySubject }                        from 'rxjs/ReplaySubject';
import { Response }                             from '@angular/http';
import { ResponseContentType }                  from '@angular/http';
import { RequestMethod }                        from '@angular/http';
import { RequestOptions }                       from '@angular/http';

import { Subject }                              from 'rxjs/Subject';

let DocumentUploadWorker = require('worker-loader!./documentupload.worker.ts');



interface WebWorkerMessage {
  type: string;
  payload: any;
  id?: number;
}



class WebWorkerClient {

  private _nextMessageId : number = 0;

  /**
   * Factory Method
   */

  static create(zone: NgZone) : WebWorkerClient {
    return new WebWorkerClient(new DocumentUploadWorker (), zone);
  }

  /**
   * Constructor
   */

  /**
   * Spawn a worker to handle chunked file uploads
   * Initialise a stream to store upload responses
   */
  constructor (private _worker : Worker, private zone : NgZone) {}


  /**
   * postMessage<T> : Observable<T>
   * Create an observable that upon subscription will notify the subscriber
   * of worker message and error responses
   */
  public postMessage<T> (type : string, payload : any) : Observable <T> {

    let _self = this;

    return new Observable (subscriber => {

      const id = this._nextMessageId++;

      // create event handlers
      const onMessage = (response : MessageEvent) => {
        const {type: responseType, id: responseId, payload: responsePayload} = response.data as WebWorkerMessage;
        if (responseType === 'error' && id  === responseId) {
           _self.zone.run (() => {
            console.log ('Application error received from worker in main thread. Notifying subscriber');
            subscriber.error (JSON.stringify(responsePayload));
            this._error.next (true);
        });
        }
        else if (type === responseType && id === responseId) {
          _self.zone.run(() => {
            console.log('Main thread is notifying subscriber of response payload...');
            subscriber.next(responsePayload);
            subscriber.complete();
          });
        }
      }

      const onError = (error : ErrorEvent) => {
        console.log ('Main thread has received an error from worker');
        _self.zone.run (() => {
          subscriber.error (error);
        });
      }

      // initialise event handlers
      _self._worker.addEventListener('message', onMessage);
      _self._worker.addEventListener('error', onError);

      // post the actual message
      console.log ('Posting actual message with id %d', id);
      _self._worker.postMessage ({type, id, payload});

      // at completion remove event listeners
      return () => {
        console.log ('Completed observing worker response, closing worker');
        _self._worker.removeEventListener('message', onMessage);
        _self._worker.removeEventListener('error', onError);
      };
    });
  }
}

客户代码

public uploadBuffer (buffer : Observable<FileChunk>, ticketId, uploadUrl : string) : void {

    let worker : WebWorkerClient = WebWorkerClient.create (this.zone);

    buffer
    .flatMap (chunk => this.createMessage (chunk, ticketId, uploadUrl))                     // create a deferred message for each chunk
    .subscribe (
      (message) => {
        let observeMessage = worker.postMessage<boolean>('UPLOAD',message).publishReplay(1);
        observeMessage.subscribe (
          (item) => { console.log ('Uploaded item %s', item); },
          (error) => { console.log ('Upload error %s', error); },
          () => {}
        );
        observeMessage.connect ();
      },
      (error) => {
        console.log ('Error received creating message', error);
      },
      () => {
        console.log ('Completed processing message');
      }
    );
  }

  /**
   * createMessage (chunk : FileChunk, ticketId, uploadUrl : string)
   * defer creation of a request message until subscription.
   */
  public createMessage (chunk: FileChunk, ticketId : string, uploadUrl : string) : Observable<any> {

    return Observable.defer (
      () => {

        const message = {
          url: uploadUrl,
          chunk: chunk.chunk,
          chunkid: chunk.chunkId,
          documenttypeid: chunk.documentType,
          ticketid: ticketId,
          totalchunks: chunk.totalChunks
        };

        console.log ('creating message for chunk %d', message.chunkid);

        return Observable.of (message);
      }
    );
  }

更新

在客户端使用concatMap提供有序响应:

let worker : WebWorkerClient = WebWorkerClient.create (this.zone);

buffer
.concatMap (chunk => this.createMessage (chunk, ticketId, uploadUrl))
.concatMap (message => worker.postMessage<boolean> ('UPLOAD',message))
.subscribe (
  (message) => { console.log ('Message received is : ' + message); },
  (error) => { console.log ('Error received creating message', error); },
  () => { console.log ('Completed processing message'); }
);

0 个答案:

没有答案