如何在解析其他Promise前等待连接完成?
异步客户端连接的竞态问题与简洁修复方案
问题场景
当并发调用Api.request()时,原代码会多次触发客户端连接,不符合"仅初始化一次连接,所有请求复用该连接"的预期。
原代码
class Client { async connect() { return new Promise(resolve => { setTimeout(() => { console.log('connect'); resolve() }, 2000); }) } async request() { console.log('request completed') } } class Api { #client; async getClient() { if (!this.#client) { const client = new Client(); await client.connect(); this.#client = client; } return this.#client; } async request() { const client = await this.getClient(); return client.request(); } } const run = async () => { const api = new Api(); await Promise.all([api.request(), api.request(), api.request(), api.request()]); } run();
错误输出
connect request completed connect request completed connect request completed connect request completed
期望输出
connect request completed request completed request completed request completed
简洁修复方案
核心思路是用Promise存储连接状态,让所有并发请求等待同一个连接Promise,避免重复初始化。仅需修改Api类的少量代码,几乎不改动原有结构:
class Client { async connect() { return new Promise(resolve => { setTimeout(() => { console.log('connect'); resolve() }, 2000); }) } async request() { console.log('request completed') } } class Api { #clientPromise; // 存储连接Promise,而非直接存储客户端实例 async getClient() { if (!this.#clientPromise) { const client = new Client(); // 将connect的Promise保存,后续请求共享该Promise this.#clientPromise = client.connect().then(() => client); } return this.#clientPromise; } async request() { const client = await this.getClient(); return client.request(); } } const run = async () => { const api = new Api(); await Promise.all([api.request(), api.request(), api.request(), api.request()]); } run();
方案原理
- 第一次调用
getClient()时,创建Client实例并发起连接,同时将connect()的Promise(最终resolve为client实例)赋值给#clientPromise - 后续并发请求进入
getClient()时,#clientPromise已存在,直接await该Promise,所有请求共享同一个连接流程 - 连接完成后,所有请求会拿到同一个
client实例,实现复用
对比原有解决方案
此方案仅调整了Api类中客户端实例的存储方式,无需额外维护#connecting状态和请求队列,代码更简洁、可读性更强,同时完美解决竞态问题。
内容的提问来源于stack exchange,提问作者Andrew Rusinas
相关产品推荐
相关产品推荐

