Async/Menanti Konstruktor Kelas

Pada saat ini, saya'm mencoba untuk menggunakan async/menanti di dalam kelas fungsi constructor. Ini adalah agar saya bisa mendapatkan kustom e-mail tag untuk sebuah Elektron proyek I'm bekerja pada.

customElements.define('e-mail', class extends HTMLElement {
  async constructor() {
    super()

    let uid = this.getAttribute('data-uid')
    let message = await grabUID(uid)

    const shadowRoot = this.attachShadow({mode: 'open'})
    shadowRoot.innerHTML = `
      <div id="email">A random email message has appeared. ${message}</div>
    `
  }
})

Pada saat ini namun, proyek ini tidak bekerja, dengan error berikut:

Class constructor may not be an async method

Apakah ada cara untuk menghindari hal ini, sehingga saya dapat menggunakan async/menanti dalam hal ini? Bukan membutuhkan callback atau .maka()?

Mengomentari pertanyaan (6)
Larutan

Hal ini dapat tidak pernah bekerja.

The async kata kunci memungkinkan menunggu yang akan digunakan dalam sebuah fungsi ditandai sebagai async tetapi juga mengubah fungsi itu menjadi janji generator. Jadi fungsi ditandai dengan async akan kembali janji. Konstruktor di sisi lain mengembalikan objek itu adalah membangun. Dengan demikian kita memiliki situasi di mana anda ingin keduanya kembali sebuah objek dan janji: situasi yang mustahil.

Anda hanya dapat menggunakan async/menginap di mana anda dapat menggunakan menjanjikan karena mereka pada dasarnya sintaks gula untuk janji-janji. Anda dapat't menggunakan janji-janji dalam sebuah konstruktor karena sebuah konstruktor harus kembali objek yang akan dibangun, bukan janji.

Ada dua pola desain untuk mengatasi hal ini, kedua diciptakan sebelum janji-janji yang berada di sekitar.

  1. Penggunaan init() fungsi. Ini bekerja sedikit seperti jQuery's .siap(). Objek yang anda buat hanya dapat digunakan dalam it's sendiri init atau siap fungsi:

Penggunaan:

var myObj = new myClass(); myObj.init(function() { // di sini anda dapat menggunakan myObj });

Pelaksanaan:

class myClass { konstruktor () {

}

init (callback) { // lakukan sesuatu async dan panggilan callback: callback.bind(this)(); } }

  1. Menggunakan builder. I've tidak melihat hal ini banyak digunakan dalam javascript tapi ini adalah salah satu yang lebih umum pekerjaan-arounds di Jawa ketika suatu objek yang perlu dibangun secara asinkron. Tentu saja, pembangun pola ini digunakan ketika membangun sebuah objek yang memerlukan banyak parameter yang rumit. Yang persis use-case untuk asynchronous pembangun. Perbedaannya adalah bahwa async pembangun tidak kembali objek tetapi janji bahwa objek:

Penggunaan:

myClass.membangun().maka(fungsi(myObj) { // myObj dikembalikan oleh janji, // tidak oleh konstruktor // atau pembangun });

// dengan async/menunggu:

async function foo () { var myObj = menunggu myClass.membangun(); }

Pelaksanaan:

class myClass { konstruktor (async_param) { if (typeof async_param === 'undefined') { melempar Kesalahan baru('Tidak dapat dipanggil secara langsung'); } }

membangun statis () { kembali doSomeAsyncStuff() .maka(fungsi(async_result){ kembali new myClass(async_result); }); } }

Implementasi dengan async/menunggu:

class myClass { konstruktor (async_param) { if (typeof async_param === 'undefined') { melempar Kesalahan baru('Tidak dapat dipanggil secara langsung'); } }

statis async membangun () { var async_result = menunggu doSomeAsyncStuff(); kembali new myClass(async_result); } }

Catatan: meskipun dalam contoh di atas kita menggunakan menjanjikan untuk async pembangun mereka tidak benar-benar berbicara diperlukan. Anda dapat dengan mudah menulis builder yang menerima panggilan balik.


Catatan untuk memanggil fungsi di dalam fungsi statis.

Ini tidak ada sama sekali hubungannya dengan async konstruktor tapi dengan apa kata kunci ini benar-benar berarti (yang mungkin sedikit mengejutkan untuk orang-orang yang berasal dari bahasa yang melakukan auto-resolusi nama metode, yaitu, bahasa yang don't perlu ini kata kunci).

Yang ini kata kunci mengacu pada contoh objek. Tidak kelas. Oleh karena itu anda tidak bisa biasanya menggunakan ini dalam statis fungsi karena fungsi statis tidak terikat untuk setiap objek tetapi terikat langsung ke kelas.

Itu adalah untuk mengatakan, dalam kode berikut:

class A {
    static foo () {}
}

Anda tidak bisa melakukan:

var a = new A();
a.foo() // NOPE!!

sebaliknya anda perlu menyebutnya sebagai:

A.foo();

Oleh karena itu, kode berikut akan menghasilkan kesalahan:

class A {
    static foo () {
        this.bar(); // you are calling this as static
                    // so bar is undefinned
    }
    bar () {}
}

Untuk memperbaikinya anda dapat membuat bar baik reguler fungsi atau metode statis:

function bar1 () {}

class A {
    static foo () {
        bar1();   // this is OK
        A.bar2(); // this is OK
    }

    static bar2 () {}
}
Komentar (9)

Anda bisa pasti melakukan hal ini. Pada dasarnya:

class AsyncConstructor {
    constructor() {
        return (async () => {

            // All async code here
            this.value = await asyncFunction();

            return this; // when done
        })();
    }
}

untuk membuat kelas menggunakan:

let instance = await new AsyncConstructor();

Catatan: Jika anda perlu untuk menggunakan super, anda tidak bisa menyebutnya dalam async callback. Anda harus menyebutnya luar itu jadi solusi ini not 100% sempurna, tapi menurut saya itu cukup idiomatik dan saya menggunakannya sepanjang waktu dalam kode saya.

Komentar (11)

Berdasarkan komentar anda, anda mungkin harus melakukan apa yang setiap HTMLElement dengan aset loading tidak: membuat konstruktor mulai sideloading tindakan, menghasilkan beban atau kesalahan peristiwa tergantung pada hasil.

Ya, itu berarti menggunakan janji-janji, tapi itu juga berarti "tidak melakukan hal-hal dengan cara yang sama seperti setiap elemen HTML", sehingga anda're di perusahaan yang baik. Misalnya:

var img = new Image();
img.onload = function(evt) { ... }
img.addEventListener("load", evt => ... );
img.onerror = function(evt) { ... }
img.addEventListener("error", evt => ... );
img.src = "some url";

ini kicks off asynchronous beban dari sumber aset itu, bila berhasil, berakhir di onload dan ketika itu terjadi, berakhir di onerror. Jadi, membuat kelas anda sendiri melakukan hal ini terlalu:

class EMailElement extends HTMLElement {
  constructor() {
    super();
    this.uid = this.getAttribute('data-uid');
  }

  setAttribute(name, value) {
    super.setAttribute(name, value);
    if (name === 'data-uid') {
      this.uid = value;
    }
  }

  set uid(input) {
    if (!input) return;
    const uid = parseInt(input);
    // don't fight the river, go with the flow
    let getEmail = new Promise( (resolve, reject) => {
      yourDataBase.getByUID(uid, (err, result) => {
        if (err) return reject(err);
        resolve(result);
      });
    });
    // kick off the promise, which will be async all on its own
    getEmail()
    .then(result => {
      this.renderLoaded(result.message);
    })
    .catch(error => {
      this.renderError(error);
    });
  }
};

customElements.define('e-mail', EmailElement);

Dan kemudian anda membuat renderLoaded/renderError fungsi berurusan dengan peristiwa panggilan dan bayangan dom:

  renderLoaded(message) {
    const shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.innerHTML = `
      <div class="email">A random email message has appeared. ${message}</div>
    `;
    // is there an ancient event listener?
    if (this.onload) {
      this.onload(...);
    }
    // there might be modern event listeners. dispatch an event.
    this.dispatchEvent(new Event('load', ...));
  }

  renderFailed() {
    const shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.innerHTML = `
      <div class="email">No email messages.</div>
    `;
    // is there an ancient event listener?
    if (this.onload) {
      this.onerror(...);
    }
    // there might be modern event listeners. dispatch an event.
    this.dispatchEvent(new Event('error', ...));
  }

Juga catatan saya mengubah anda id untuk kelas, karena kecuali anda menulis beberapa kode aneh untuk hanya memungkinkan satu contoh dari <e-mail> elemen pada halaman, anda dapat't menggunakan pengidentifikasi unik dan kemudian menetapkan untuk sekelompok elemen.

Komentar (0)

Karena async fungsi yang menjanjikan, anda dapat membuat fungsi statis di kelas anda yang mengeksekusi async function yang mengembalikan instance dari kelas:

class Yql {
  constructor () {
    // Set up your class
  }

  static init () {
    return (async function () {
      let yql = new Yql()
      // Do async stuff
      await yql.build()
      // Return instance
      return yql
    }())
  }  

  async build () {
    // Do stuff with await if needed
  }
}

async function yql () {
  // Do this instead of "new Yql()"
  let yql = await Yql.init()
  // Do stuff with yql instance
}

yql()

Sebut dengan biarkan yql = menunggu Yql.init() dari sebuah async function.

Komentar (0)

menggunakan async metode dalam membangun???

constructor(props) {
    super(props);
    (async () => await this.qwe(() => console.log(props), () => console.log(props)))();
}

async qwe(q, w) {
    return new Promise((rs, rj) => {
        rs(q());
        rj(w());
    });
}
Komentar (0)

Saya membuat ini test-case based on @Downgoat's jawaban. Ini berjalan pada NodeJS. Ini adalah Downgoat's kode async bagian ini disediakan oleh setTimeout() call.

'use strict';
const util = require( 'util' );

class AsyncConstructor{

  constructor( lapse ){
    this.qqq = 'QQQ';
    this.lapse = lapse;
    return ( async ( lapse ) => {
      await this.delay( lapse );
      return this;
    })( lapse );
  }

  async delay(ms) {
    return await new Promise(resolve => setTimeout(resolve, ms));
  }

}

let run = async ( millis ) => {
  // Instatiate with await, inside an async function
  let asyncConstructed = await new AsyncConstructor( millis );
  console.log( 'AsyncConstructor: ' + util.inspect( asyncConstructed ));
};

run( 777 );

Saya menggunakan hal ini DAOs untuk sisi server aplikasi web. Seperti yang saya lihat DAOs, mereka masing-masing terkait dengan format record, dalam kasus saya MongoDB koleksi seperti misalnya memasak. Sebuah cooksDAO contoh memegang memasak's data. Dalam pikiran gelisah saya, saya akan mampu untuk instantiate memasak's DAO menyediakan cookId sebagai argumen, dan instansiasi akan membuat objek dan mengisinya dengan memasak's data. Dengan demikian perlu untuk menjalankan async barang-barang ke konstruktor. Aku ingin menulis:

let cook = new cooksDAO( '12345' );  

untuk memiliki properti yang tersedia seperti memasak.getDisplayName(). Dengan solusi ini harus saya lakukan:

let cook = await new cooksDAO( '12345' );  

yang sangat mirip dengan ideal. Juga, aku harus melakukan ini dalam sebuah async fungsi.

Saya B-rencana untuk meninggalkan loading data dari konstruktor, berdasarkan @slebetman saran untuk menggunakan fungsi init, dan melakukan sesuatu seperti ini:

let cook = new cooksDAO( '12345' );  
async cook.getData();

yang doesn't melanggar aturan.

Komentar (0)

Jika anda dapat menghindari memperpanjang, anda dapat menghindari semua kelas bersama-sama dan menggunakan fungsi komposisi sebagai constructors. Anda dapat menggunakan variabel-variabel dalam lingkup yang bukan anggota kelas:

js async function buildA(...) { const data = menunggu fetch(...); kembali { getData: fungsi() { mengembalikan data; } } }

dan sederhana menggunakannya sebagai js const a = menunggu buildA(...);

Jika anda're menggunakan naskah atau aliran, anda bahkan dapat menegakkan antarmuka constructors `` Antarmuka { getData: objek; }

async function buildA0(...): Janji { ... } async function buildA1(...): Janji { ... } ... ``

Komentar (0)

Anda dapat membuat sebuah async init() {... kembali ini;} metode, maka bukan tidak new MyClass().init() setiap kali anda'd biasanya hanya mengatakan new MyClass().

Ini tidak bersih karena hal itu bergantung pada setiap orang yang menggunakan kode anda, dan diri anda sendiri, untuk selalu instantiate obyek seperti begitu. Namun jika anda're hanya menggunakan objek ini di tempat tertentu atau dua dalam kode anda, hal itu harus baik-baik saja.

Komentar (0)

Anda dapat segera memanggil anonim async function yang mengembalikan pesan dan set ke pesan variabel. Anda mungkin ingin mengambil melihat segera dipanggil fungsi ekspresi (IEFES), dalam kasus anda tidak terbiasa dengan pola ini. Ini akan bekerja seperti pesona.

var message = (async function() { return await grabUID(uid) })()
Komentar (0)

Variasi pada pembangun pola, menggunakan panggilan():

function asyncMethod(arg) {
    function innerPromise() { return new Promise((...)=> {...}) }
    innerPromise().then(result => {
        this.setStuff(result);
    }
}

const getInstance = async (arg) => {
    let instance = new Instance();
    await asyncMethod.call(instance, arg);
    return instance;
}
Komentar (0)

@slebetmen's jawaban yang diterima menjelaskan dengan baik mengapa hal ini doesn't bekerja. Selain dua pola yang disajikan dalam jawaban itu, pilihan lain adalah untuk hanya mengakses async sifat melalui custom async getter. Konstruktor() kemudian dapat memicu async penciptaan sifat, tetapi pengambil kemudian memeriksa untuk melihat jika properti yang tersedia sebelum menggunakan atau kembali.

Pendekatan ini sangat berguna ketika anda ingin menginisialisasi sebuah objek global setelah pada startup, dan anda ingin melakukannya di dalam sebuah modul. Bukan inisialisasi dalam index.js dan melewati misalnya di tempat-tempat yang membutuhkannya, hanya memerlukan modul anda dimanapun global objek yang dibutuhkan.

Penggunaan

const instance = new MyClass();
const prop = await instance.getMyProperty();

Implementasi

class MyClass {
  constructor() {
    this.myProperty = null;
    this.myPropertyPromise = this.downloadAsyncStuff();
  }
  async downloadAsyncStuff() {
    // await yourAsyncCall();
    this.myProperty = 'async property'; // this would instead by your async call
    return this.myProperty;
  }
  getMyProperty() {
    if (this.myProperty) {
      return this.myProperty;
    } else {
      return this.myPropertyPromise;
    }
  }
}
Komentar (0)

Anda harus menambahkan kemudian fungsi untuk contoh. Berjanji akan mengenali ini sebagai thenable objek dengan Janji.mengatasi secara otomatis

const asyncSymbol = Symbol();
class MyClass {
    constructor() {
        this.asyncData = null
    }
    then(resolve, reject) {
        return (this[asyncSymbol] = this[asyncSymbol] || new Promise((innerResolve, innerReject) => {
            this.asyncData = { a: 1 }
            setTimeout(() => innerResolve(this.asyncData), 3000)
        })).then(resolve, reject)
    }
}

async function wait() {
    const asyncData = await new MyClass();
    alert('run 3s later')
    alert(asyncData.a)
}
Komentar (1)

Lain jawaban yang hilang yang jelas. Hanya panggilan async fungsi dari constructor:

constructor() {
    setContentAsync();
}

async setContentAsync() {
    let uid = this.getAttribute('data-uid')
    let message = await grabUID(uid)

    const shadowRoot = this.attachShadow({mode: 'open'})
    shadowRoot.innerHTML = `
      <div id="email">A random email message has appeared. ${message}</div>
    `
}
Komentar (5)