类构造函数异步方法的Promise等待最佳实践及ReferenceError问题排查
嘿,我来帮你捋清楚这个问题,顺便给你几个靠谱的最佳实践方案~
首先说你遇到的错误为啥会发生:你在Promise.all().then()里调用的resolve()根本找不到!这个resolve是属于外面那个包裹new MyClass()的Promise的,但构造函数内部根本访问不到外部的这个resolve变量,所以直接报了ReferenceError。而且还有个关键点:JavaScript的构造函数不能是异步函数(加async关键字不行),也不能直接返回Promise(虽然语法允许,但不符合类的设计逻辑,容易坑到后续用这个类的人),所以你原来的思路走不通。
下面是几种推荐的实现方式,按靠谱程度排序:
方案1:用静态工厂方法(最推荐)
搞一个静态的async方法,在里面先做完所有异步请求,再创建类实例,这样外部直接await这个方法就能拿到初始化完成的实例了。
class MyClass { // 可以把构造函数设为私有(如果环境支持),强制大家用工厂方法创建实例 constructor() { // 这里放同步的初始化逻辑就行 } // 静态工厂方法,专门处理异步初始化 static async create() { // 先把ajax封装成Promise,顺便处理错误 const getApi = (url) => new Promise((resolve, reject) => { $.ajax({ type: "GET", url, success: resolve, error: reject // 别忘了处理请求失败的情况! }); }); // 并行请求两个接口 const [res1, res2] = await Promise.all([getApi(api1), getApi(api2)]); console.log("两个请求都完成啦"); // 创建实例,把拿到的数据绑定上去 const instance = new MyClass(); instance.data1 = res1; instance.data2 = res2; return instance; } } // 用的时候这么写 $(async function() { try { const myClass = await MyClass.create(); console.log("所有初始化都完成了!"); // 这里放心用myClass实例就行 } catch (err) { // 这里捕获任何请求失败的错误 console.error("初始化出错了:", err); } });
这个方式的好处是:
- 逻辑清晰,用的人一看就知道创建实例是异步操作
- 错误处理集中,用try/catch就能搞定所有异步错误
- 可以把异步拿到的数据直接绑定到实例上,后续用起来方便
方案2:构造函数里加个Promise属性,外部等它
如果不想用静态方法,也可以在构造函数里初始化一个Promise属性,外部代码等待这个属性完成就行。
class MyClass { constructor() { // 把初始化的Promise作为实例属性暴露出去 this.initDone = this.init(); } // 封装异步初始化逻辑 async init() { const getApi = (url) => new Promise((resolve, reject) => { $.ajax({ type: "GET", url, success: resolve, error: reject }); }); const [res1, res2] = await Promise.all([getApi(api1), getApi(api2)]); console.log("两个请求完成"); this.data1 = res1; this.data2 = res2; } } // 使用方式 $(async function() { try { const myClass = new MyClass(); await myClass.initDone; // 必须等这个Promise完成再用实例 console.log("全部完成!"); } catch (err) { console.error("初始化失败:", err); } });
这个方式要注意:必须提醒用这个类的人要等待initDone属性,不然实例可能还没初始化好就被用了,容易出问题。
为啥不直接让构造函数返回Promise?
虽然JS允许构造函数返回非this的对象(比如Promise),但这样new MyClass()得到的是Promise而不是MyClass实例,完全违背了类的使用预期,别人用的时候很容易搞错,所以绝对不推荐这么干。
额外小建议
- 千万不要忽略错误处理!你原来的代码里没处理ajax的error,一旦请求失败,Promise会一直挂着,加上
error: reject才能正确捕获错误。 - 把ajax封装成工具函数,避免重复写Promise的代码,看起来更清爽。
内容的提问来源于stack exchange,提问作者whitebear
相关产品推荐
相关产品推荐

