JS裡的非同步例項化
阿新 • • 發佈:2021-07-02
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
})();
該作者也提供了一個基類用來繼承出非同步建構函式,可以到原文去看看。