- A+
所属分类:Web前端
JS里的异步构造函数
众所周知,Js的构造函数是不能加上async/await来实现异步实例化的,一般当需要一个对象的属性是异步的结果时可以这样写:
//! 一个需要指定时间后返回的异步函数 function delay(timeout) { return new Promise((resolve) => setTimeout(() => resolve("end"), timeout)); } class Test { async init() { this.end = await delay(1000); } } (async function () { const test = new Test(); //? 实例化 await test.init(); //? 初始化end属性 console.log(test.end); })();
但是当我想要在实例化时就调用该属性时就还要调用一次init(),这未免太麻烦了,所以想要在实例化时就把该属性赋值,就像这样const test = await new Test()
。
这时找到了这篇文章,该作者提供了这样一段代码来实现了异步构造函数:
class MyClass { constructor(timeout) { this.completed = false const init = (async () => { await delay(timeout) this.completed = true delete this.then return this })() this.then = init.then.bind(init) } } (async function(){ const myclass = await new MyClass(1000); })()
在解释这段代码前就得说说PromiseLike了:一个有then方法,且该方法接收resolve,reject两个参数的对象,就像这样:
const PromiseLike = { then(resolve) { resolve("i am PromiseLike"); }, }; (async function () { const something = await PromiseLike; console.log(something); // i am PromiseLike })();
即使PromiseLike不是一个函数,它也会被await调用对象里的then方法并resolve出结果
现在回到刚才那段代码,注意到它最后的一段了吗
this.then = init.then.bind(init)
这句话把一个异步函数init
的then
给了实例,所以在调用new Myclass
后得到的实例上有着一个then方法,这个then方法又会被前面的await
解析,这时实质上解析的就是init
这个异步函数的then
而这个then
返回的是该类的实例化删除then
后的this
。
这样await new MyClass()
会等到init
的异步执行完毕才会返回值,这个返回值是init
的resolve。
总结一下:该方法其实仍然是同步实例化出了对象,但是await
会马上异步执行实例化里then
,然后返回出then
里删除了then
方法的this
,这样就做到了一句话进行实例化并初始化异步属性。
知道了这个原理后,最初的问题就解决了:
class Test { constructor() { const init = (async () => { this.end = await delay(1000); delete this.then; return this; })(); this.then = init.then.bind(init); } } (async function () { const test = await new Test(); console.log(test.end); // end })();
该作者也提供了一个基类用来继承出异步构造函数,可以到原文去看看。