gpt4 book ai didi

angular - 使用 Angular2/RxJS 读取缓冲响应

转载 作者:太空狗 更新时间:2023-10-29 17:48:42 25 4
gpt4 key购买 nike

我正在构建一个从后端读取数据的网站。该数据是即时计算的,并以缓冲的方式发送回客户端。 IE。一旦计算出第一个 block ,它就会被发送到客户端,然后计算下一个 block 并将其发送到客户端。整个过程发生在同一个 HTTP 请求中。客户端不应等待完整的响应完成,而应在发送后立即自行处理每个 block 。此类响应通常可以使用 XHR 进度处理程序(例如 How to get progress from XMLHttpRequest )使用。

我如何使用 RxJS 和 Observables 在 Angular2 中使用 HttpModule 使用这样的响应?


编辑:peeskillet 在下面给出了出色而详细的答案。此外,我做了一些进一步的挖掘,发现了一个feature request for the HttpModule of Angular。和一个 StackOverflow question with another approach on how to solve it .

最佳答案

注意:以下答案只是一个 POC。它旨在对 Http 的体系结构进行教育,并提供一个简单的工作 POC 实现。人们应该看看 XHRConnection 的来源了解实现此功能时还应考虑的其他事项。

在尝试实现这一点时,我看不到任何直接接入 XHR 的方法。看来我们可能只需要提供一些与使用 Http 相关的组件的自定义实现。 .我们应该考虑的三个主要组成部分是

  • Connection
  • ConnectionBackend
  • Http

Http需要 ConnectionBackend作为其构造函数的参数。发出请求时,用 get 表示, Http创建与 ConnectionBackend.createConnection 的连接,并返回 Observable Connection的属性(property)(这是从 createConnection 返回的)。在最精简(简化)的 View 中,它看起来像这样

class XHRConnection implements Connection {
response: Observable<Response>;
constructor( request, browserXhr) {
this.response = new Observable((observer: Observer<Response>) => {
let xhr = browserXhr.create();
let onLoad = (..) => {
observer.next(new Response(...));
};
xhr.addEventListener('load', onLoad);
})
}
}

class XHRBackend implements ConnectionBackend {
constructor(private browserXhr) {}
createConnection(request): XHRConnection {
return new XHRConnection(request, this.broswerXhr).response;
}
}

class Http {
constructor(private backend: ConnectionBackend) {}

get(url, options): Observable<Response> {
return this.backend.createConnection(createRequest(url, options)).response;
}
}

所以了解了这个架构,我们可以尝试实现类似的东西。

对于 Connection ,这是 POC。为简洁起见省略了导入,但在大多数情况下,所有内容都可以从 @angular/http 导入, 和 Observable/Observer可以从rxjs/{Type}导入.

export class Chunk {
data: string;
}

export class ChunkedXHRConnection implements Connection {
request: Request;
response: Observable<Response>;
readyState: ReadyState;

chunks: Observable<Chunk>;

constructor(req: Request, browserXHR: BrowserXhr, baseResponseOptions?: ResponseOptions) {
this.request = req;
this.chunks = new Observable<Chunk>((chunkObserver: Observer<Chunk>) => {
let _xhr: XMLHttpRequest = browserXHR.build();
let previousLen = 0;
let onProgress = (progress: ProgressEvent) => {
let text = _xhr.responseText;
text = text.substring(previousLen);
chunkObserver.next({ data: text });
previousLen += text.length;

console.log(`chunk data: ${text}`);
};
_xhr.addEventListener('progress', onProgress);
_xhr.open(RequestMethod[req.method].toUpperCase(), req.url);
_xhr.send(this.request.getBody());
return () => {
_xhr.removeEventListener('progress', onProgress);
_xhr.abort();
};
});
}
}

这里我们只是订阅了 XHR progress事件。自 XHR.responseText吐出整个连接的文本,我们只是substring获取 block ,并通过 Observer 发出每个夹头.

对于 XHRBackend ,我们有以下内容(没什么了不起的)。同样,所有内容都可以从 @angular/http 导入;

@Injectable()
export class ChunkedXHRBackend implements ConnectionBackend {
constructor(
private _browserXHR: BrowserXhr, private _baseResponseOptions: ResponseOptions,
private _xsrfStrategy: XSRFStrategy) {}

createConnection(request: Request): ChunkedXHRConnection {
this._xsrfStrategy.configureRequest(request);
return new ChunkedXHRConnection(request, this._browserXHR, this._baseResponseOptions);
}
}

对于 Http ,我们将扩展它,添加一个 getChunks方法。如果需要,您可以添加更多方法。

@Injectable()
export class ChunkedHttp extends Http {
constructor(protected backend: ChunkedXHRBackend, protected defaultOptions: RequestOptions) {
super(backend, defaultOptions);
}

getChunks(url, options?: RequestOptionsArgs): Observable<Chunk> {
return this.backend.createConnection(
new Request(mergeOptions(this.defaultOptions, options, RequestMethod.Get, url))).chunks;
}
}

mergeOptions方法可以在 Http source 中找到.

现在我们可以为它创建一个模块。用户应该直接使用ChunkedHttp而不是 Http .但是因为不要试图覆盖 Http token ,您仍然可以使用 Http如果需要的话。

@NgModule({
imports: [ HttpModule ],
providers: [
{
provide: ChunkedHttp,
useFactory: (backend: ChunkedXHRBackend, options: RequestOptions) => {
return new ChunkedHttp(backend, options);
},
deps: [ ChunkedXHRBackend, RequestOptions ]
},
ChunkedXHRBackend
]
})
export class ChunkedHttpModule {
}

我们导入 HttpModule因为它提供了我们需要注入(inject)的其他服务,但我们不想在不需要时重新实现这些服务。

要测试只需导入 ChunkedHttpModule进入AppModule .另外为了测试我使用了以下组件

@Component({
selector: 'app',
encapsulation: ViewEncapsulation.None,
template: `
<button (click)="onClick()">Click Me!</button>
<h4 *ngFor="let chunk of chunks">{{ chunk }}</h4>
`,
styleUrls: ['./app.style.css']
})
export class App {
chunks: string[] = [];

constructor(private http: ChunkedHttp) {}

onClick() {
this.http.getChunks('http://localhost:8080/api/resource')
.subscribe(chunk => this.chunks.push(chunk.data));
}
}

我设置了一个后端端点,它只是吐出 "Message #x"每半秒分 10 个 block 。这是结果

enter image description here

似乎某处有错误。只有九个 :-)。我认为这与服务器端相关。

关于angular - 使用 Angular2/RxJS 读取缓冲响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39820718/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com