1. 程式人生 > 其它 >JS裡的非同步例項化

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)

這句話把一個非同步函式initthen給了例項,所以在呼叫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的結合