天天看點

JS裡的異步執行個體化

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
})();
           

該作者也提供了一個基類用來繼承出異步構造函數,可以到原文去看看。

參考:異步構造函數 - 構造函數與Promise的結合