Javascript/Angular中是否存在可订阅的request sent事件钩子?
核心结论
浏览器没有向全局JavaScript上下文暴露通用的request-sent事件,但Angular生态提供了原生支持的能力,完全可以实现你要求的请求发送顺序控制逻辑。
请求调用顺序与实际发送顺序不一致的常见原因
- 浏览器同域名并发请求限制:HTTP/1.1协议下浏览器默认最多同时发起6个同域名请求,超出的请求会进入浏览器内部队列等待,直接打乱代码调用的顺序
- Angular HTTP拦截器异步逻辑:如果拦截器中存在异步获取鉴权令牌、异步加签等逻辑,会导致请求实际提交到网络层的顺序和代码调用顺序不一致
- HTTP/2协议优先级调度:HTTP/2会自动根据资源类型调整请求优先级,也可能修改你预期的发送顺序
Angular场景下的最优实现方案
Angular的HttpClient模块内置了HttpSentEvent事件,会在请求成功发送到服务器后立即触发,不需要等待响应返回,完全匹配你的需求。你可以基于这个事件封装一个串行请求队列:
import { Injectable } from '@angular/core'; import { HttpClient, HttpEvent, HttpEventType, HttpRequest } from '@angular/common/http'; import { Observable, Subject, queueScheduler } from 'rxjs'; import { observeOn, concatMap } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class PriorityRequestQueueService { // 请求队列容器 private requestQueue$ = new Subject<HttpRequest<unknown>>(); constructor(private http: HttpClient) { // 串行处理队列:前一个请求发送完成后再处理下一个 this.requestQueue$.pipe( observeOn(queueScheduler), concatMap(req => this.waitForRequestSent(req)) ).subscribe(); } /** * 对外暴露的入队方法,优先级高的请求先调用该方法即可保证先发送 * @param req 原始HttpRequest对象 */ public enqueue<T>(req: HttpRequest<T>): Observable<HttpEvent<T>> { return new Observable(observer => { // 提交请求到实际执行逻辑 this.http.request(req).subscribe({ next: event => observer.next(event), error: err => observer.error(err), complete: () => observer.complete() }); // 加入调度队列 this.requestQueue$.next(req); }); } // 等待请求发送完成,触发后通知队列处理下一个请求 private waitForRequestSent(req: HttpRequest<unknown>): Observable<void> { return new Observable(observer => { const sub = this.http.request(req).subscribe(event => { if (event.type === HttpEventType.Sent) { // 捕获到发送完成事件,立即通知队列处理下一个 observer.next(); observer.complete(); sub.unsubscribe(); } }); }); } }
使用示例:
// 优先级高的用户信息请求 const highPriorityReq = new HttpRequest('GET', '/api/user/info'); // 优先级低的日志上报请求 const lowPriorityReq = new HttpRequest('POST', '/api/log', { action: 'page_view' }); // 先入队高优先级请求,保证先发送 this.queueService.enqueue(highPriorityReq).subscribe(event => { // 正常处理响应即可,无需等待响应返回,下一个请求会在当前请求发出后立即发送 if (event.type === HttpEventType.Response) { console.log('用户信息返回', event.body); } }); // 后入队低优先级请求,会等待前一个请求发送完成后再发出 this.queueService.enqueue(lowPriorityReq).subscribe(event => { if (event.type === HttpEventType.Response) { console.log('日志上报完成', event.body); } });
原生JavaScript通用实现方案
如果不用Angular的HttpClient,可以基于XMLHttpRequest的loadstart事件近似判断请求已经发送到网络层,再封装队列即可:
// 封装请求,发送完成后立即resolve function sendWithSentNotify(url, options = {}) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open(options.method || 'GET', url); // 填充请求头 if (options.headers) { Object.entries(options.headers).forEach(([key, value]) => { xhr.setRequestHeader(key, value); }); } // 请求开始发送时触发,说明已经提交到网络层 xhr.onloadstart = () => resolve(); xhr.onerror = reject; xhr.onload = () => { // 自行处理响应逻辑 }; xhr.send(options.body || null); }); } // 队列实现示例 class RequestQueue { constructor() { this.queue = []; this.running = false; } enqueue(reqTask) { this.queue.push(reqTask); this.run(); } async run() { if (this.running || !this.queue.length) return; this.running = true; const currentTask = this.queue.shift(); await sendWithSentNotify(currentTask.url, currentTask.options); this.running = false; this.run(); } }
优化建议
- 如果需要支持多优先级调度,可以给每个请求增加
priority权重字段,入队时插入到队列对应位置,出队时优先取权重最高的请求 - 不需要控制顺序的普通请求不需要走队列,避免不必要的性能损耗
内容的提问来源于stack exchange,提问作者gib65
相关产品推荐
相关产品推荐

