Async / Await Class Constructor

169

Saat ini, saya mencoba untuk menggunakan async/awaitfungsi konstruktor kelas. Ini agar saya bisa mendapatkan e-mailtag khusus untuk proyek Elektron yang sedang saya kerjakan.

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>
    `
  }
})

Namun saat ini, proyek tidak berfungsi, dengan kesalahan berikut:

Class constructor may not be an async method

Apakah ada cara untuk menghindari ini sehingga saya dapat menggunakan async / menunggu di dalam ini? Alih-alih membutuhkan panggilan balik atau .then ()?

Alexander Craggs
sumber
6
Tujuan konstruktor adalah untuk mengalokasikan Anda sebuah objek dan kemudian segera kembali. Bisakah Anda lebih spesifik tentang mengapa Anda berpikir konstruktor Anda harus async? Karena kami hampir dijamin berurusan dengan masalah XY di sini.
Mike 'Pomax' Kamermans
4
@ Mike'Pomax'Kamermans Itu sangat mungkin. Pada dasarnya, saya perlu query database untuk mendapatkan metadata yang diperlukan untuk memuat elemen ini. Meminta basis data adalah operasi asinkron, dan karenanya saya memerlukan beberapa cara untuk menunggu ini diselesaikan sebelum membangun elemen. Saya lebih suka tidak menggunakan panggilan balik, karena saya telah menggunakan menunggu / async sepanjang sisa proyek dan ingin menjaga kontinuitas.
Alexander Craggs
@ Mike'Pomax'Kamermans Konteks penuh dari ini adalah klien email, di mana setiap elemen HTML terlihat mirip <e-mail data-uid="1028"></email>dan dari sana diisi dengan informasi menggunakan customElements.define()metode.
Alexander Craggs
Anda tidak ingin konstruktor menjadi async. Buat konstruktor sinkron yang mengembalikan objek Anda dan kemudian gunakan metode seperti .init()untuk melakukan hal-hal async. Ditambah lagi, karena Anda adalah HTMLElement subkelompok, sangat mungkin bahwa kode yang menggunakan kelas ini tidak tahu itu adalah hal yang async sehingga Anda mungkin harus mencari solusi yang sama sekali berbeda.
jfriend00

Jawaban:

262

Ini tidak akan pernah berhasil.

Kata asynckunci memungkinkan awaituntuk digunakan dalam fungsi yang ditandai sebagai asynctetapi juga mengubah fungsi itu menjadi generator janji. Jadi fungsi yang ditandai dengan asyncakan mengembalikan janji. Konstruktor di sisi lain mengembalikan objek yang dibangunnya. Dengan demikian kami memiliki situasi di mana Anda ingin mengembalikan objek dan janji: situasi yang mustahil.

Anda hanya dapat menggunakan async / menunggu di mana Anda dapat menggunakan janji karena itu pada dasarnya adalah sintaksis gula untuk janji. Anda tidak dapat menggunakan janji dalam konstruktor karena konstruktor harus mengembalikan objek yang akan dibangun, bukan janji.

Ada dua pola desain untuk mengatasi ini, keduanya diciptakan sebelum janji ada.

  1. Penggunaan suatu init()fungsi. Ini bekerja sedikit seperti jQuery .ready(). Objek yang Anda buat hanya dapat digunakan di dalamnya initatauready berfungsi:

    Pemakaian:

    var myObj = new myClass();
    myObj.init(function() {
        // inside here you can use myObj
    });

    Penerapan:

    class myClass {
        constructor () {
    
        }
    
        init (callback) {
            // do something async and call the callback:
            callback.bind(this)();
        }
    }
  2. Gunakan pembangun. Saya belum pernah melihat ini banyak digunakan dalam javascript tapi ini adalah salah satu yang paling umum di Jawa ketika sebuah objek perlu dibangun secara tidak sinkron. Tentu saja, pola pembangun digunakan ketika membangun objek yang membutuhkan banyak parameter rumit. Yang persis seperti kasus penggunaan untuk pembangun asinkron. Perbedaannya adalah bahwa pembangun async tidak mengembalikan objek tetapi janji objek itu:

    Pemakaian:

    myClass.build().then(function(myObj) {
        // myObj is returned by the promise, 
        // not by the constructor
        // or builder
    });
    
    // with async/await:
    
    async function foo () {
        var myObj = await myClass.build();
    }

    Penerapan:

    class myClass {
        constructor (async_param) {
            if (typeof async_param === 'undefined') {
                throw new Error('Cannot be called directly');
            }
        }
    
        static build () {
            return doSomeAsyncStuff()
               .then(function(async_result){
                   return new myClass(async_result);
               });
        }
    }

    Implementasi dengan async / menunggu:

    class myClass {
        constructor (async_param) {
            if (typeof async_param === 'undefined') {
                throw new Error('Cannot be called directly');
            }
        }
    
        static async build () {
            var async_result = await doSomeAsyncStuff();
            return new myClass(async_result);
        }
    }

Catatan: meskipun dalam contoh di atas kami menggunakan janji untuk pembangun async, mereka tidak secara tegas diperlukan. Anda dapat dengan mudah menulis pembangun yang menerima panggilan balik.


Catatan tentang fungsi panggilan di dalam fungsi statis.

Ini tidak ada hubungannya dengan konstruktor async tetapi dengan apa arti kata kunci thissebenarnya (yang mungkin sedikit mengejutkan bagi orang-orang yang berasal dari bahasa yang melakukan resolusi otomatis dari nama metode, yaitu, bahasa yang tidak memerlukan thiskata kunci).

Kata thiskunci mengacu pada objek yang dipakai. Bukan kelas. Karena itu Anda biasanya tidak dapat menggunakanthis fungsi statis di dalam karena fungsi statis tidak terikat pada objek apa pun tetapi terikat langsung ke kelas.

Artinya, dalam kode berikut:

class A {
    static foo () {}
}

Anda tidak dapat melakukan:

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

alih-alih, Anda perlu menyebutnya sebagai:

A.foo();

Karenanya, 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 barfungsi biasa atau metode statis:

function bar1 () {}

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

    static bar2 () {}
}
Slebetman
sumber
perhatikan bahwa berdasarkan komentar, idenya adalah bahwa ini adalah elemen html, yang biasanya tidak memiliki manual init()tetapi memiliki fungsi yang dikaitkan dengan beberapa atribut tertentu seperti srcatau href(dan dalam hal ini, data-uid) yang berarti menggunakan setter yang keduanya mengikat dan memulai init setiap kali nilai baru terikat (dan mungkin selama konstruksi juga, tetapi tentu saja tanpa menunggu di jalur kode yang dihasilkan)
Mike 'Pomax' Kamermans
Anda harus mengomentari mengapa jawaban di bawah ini tidak mencukupi (jika ya). Atau mengatasinya sebaliknya.
Augie Gardner
Saya ingin tahu mengapa binddiperlukan dalam contoh pertama callback.bind(this)();? Sehingga Anda bisa melakukan hal-hal seperti this.otherFunc()di dalam callback?
Alexander Craggs
1
@AlexanderCraggs Hanya kenyamanan sehingga thismengacu pada callback myClass. Jika Anda selalu menggunakan myObjbukannya thisAnda tidak membutuhkannya
slebetman
1
Saat ini adalah batasan pada bahasa tetapi saya tidak melihat mengapa di masa depan Anda tidak dapat memiliki const a = await new A()dengan cara yang sama kami memiliki fungsi reguler dan fungsi async.
7ynk3r
138

Anda pasti bisa melakukan ini. Pada dasarnya:

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

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

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

untuk membuat penggunaan kelas:

let instance = await new AsyncConstructor();

Solusi ini memiliki beberapa jatuh pendek:

superCatatan : Jika Anda perlu menggunakan super, Anda tidak dapat memanggilnya dalam panggilan balik async.

Catatan TypeScript: ini menyebabkan masalah dengan TypeScript karena konstruktor mengembalikan tipe Promise<MyClass>bukan MyClass. Tidak ada cara pasti untuk menyelesaikan ini yang saya tahu. Salah satu cara potensial yang disarankan oleh @blitter adalah dengan meletakkannya /** @type {any} */di awal badan konstruktor— Saya tidak tahu apakah ini berfungsi dalam semua situasi.

Downgoat
sumber
1
@PAStheLoD Saya tidak berpikir itu akan menyelesaikan ke objek tanpa kembali tetapi Anda mengatakan bahwa itu jadi saya akan meninjau spesifikasi dan memperbarui ...
Downgoat
2
@JuanLanus blok async akan secara otomatis menangkap parameter sehingga untuk argumen x Anda hanya perlu melakukanconstructor(x) { return (async()=>{await f(x); return this})() }
Downgoat
1
@PAStheLoD: return thisdiperlukan, karena ketika constructormelakukannya secara otomatis untuk Anda, async IIFE itu tidak, dan Anda akhirnya akan mengembalikan objek kosong.
Dan Dascalescu
1
Saat ini pada TS 3.5.1 penargetan ES5, ES2017, ES2018 (dan mungkin yang lain, tetapi saya belum memeriksa) jika Anda melakukan pengembalian dalam konstruktor Anda mendapatkan pesan kesalahan ini: "Jenis kembali tanda tangan konstruktor harus ditugaskan ke tipe instance dari kelas. " Tipe IIFE adalah Janji <ini>, dan karena kelasnya bukan Janji <T>, saya tidak melihat bagaimana itu bisa bekerja. (Apa yang bisa Anda kembalikan selain 'ini'?) Jadi ini berarti kedua pengembalian tidak perlu. (Yang luar menjadi sedikit lebih buruk, karena mengarah ke kesalahan kompilasi.)
PAStheLoD
3
@ PAStheLoD ya, itu batasan naskah. Biasanya dalam JS sebuah kelas Tharus kembali Tketika dibangun tetapi untuk mendapatkan kemampuan async kita mengembalikan Promise<T>yang memutuskan this, tapi itu membingungkan naskah. Anda memang membutuhkan pengembalian luar jika tidak Anda tidak akan tahu kapan janji selesai selesai — sebagai akibatnya pendekatan ini tidak akan bekerja pada TypeScript (kecuali ada beberapa peretasan dengan mungkin ketik aliasing?). Bukan ahli naskah sehingga tidak bisa berbicara tentang hal itu
Downgoat
7

Karena fungsi async adalah janji, Anda bisa membuat fungsi statis di kelas Anda yang mengeksekusi fungsi async yang mengembalikan instance 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()

Panggilan dengan let yql = await Yql.init()dari fungsi async.

Vidar
sumber
5

Berdasarkan komentar Anda, Anda mungkin harus melakukan apa yang dilakukan oleh setiap HTMLElement lainnya dengan pemuatan aset: membuat konstruktor memulai tindakan sampingan, menghasilkan peristiwa pemuatan atau kesalahan tergantung pada hasilnya.

Ya, itu berarti menggunakan janji, tetapi juga berarti "melakukan hal-hal dengan cara yang sama seperti setiap elemen HTML lainnya", jadi Anda berada 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 memulai muatan asinkron aset sumber yang, ketika berhasil, berakhir onloaddan ketika salah, berakhir onerror. Jadi, buat kelas Anda sendiri melakukan ini juga:

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 fungsi renderLoaded / renderError berurusan dengan panggilan acara dan dom bayangan:

  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', ...));
  }

Perhatikan juga bahwa saya mengubah Anda idmenjadi a class, karena kecuali Anda menulis beberapa kode aneh hanya mengizinkan satu contoh <e-mail>elemen Anda pada halaman, Anda tidak dapat menggunakan pengidentifikasi unik dan kemudian menetapkannya ke sekelompok elemen.

Mike 'Pomax' Kamermans
sumber
2

Saya membuat test case ini berdasarkan jawaban @ Downgoat.
Ini berjalan pada NodeJS. Ini adalah kode Downgoat di mana bagian async disediakan oleh setTimeout()panggilan.

'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 );

Kasing penggunaan saya adalah DAO untuk sisi server dari aplikasi web.
Seperti yang saya lihat DAO, mereka masing-masing terkait dengan format rekaman, dalam kasus saya koleksi MongoDB seperti misalnya juru masak.
Contoh cooksDAO memegang data juru masak.
Dalam benak saya yang gelisah, saya dapat membuat Instansiasi seorang juru masak DAO yang menyediakan CookId sebagai argumen, dan Instansiasi akan membuat objek dan mengisinya dengan data si juru masak.
Jadi kebutuhan untuk menjalankan barang-barang async ke dalam konstruktor.
Saya ingin menulis:

let cook = new cooksDAO( '12345' );  

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

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

yang sangat mirip dengan yang ideal.
Juga, saya perlu melakukan ini di dalam sebuahasync fungsi.

B-plan saya adalah membiarkan data keluar dari konstruktor, berdasarkan saran @slebetman untuk menggunakan fungsi init, dan melakukan sesuatu seperti ini:

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

yang tidak melanggar aturan.

Juan Lanus
sumber
2

menggunakan metode async 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());
    });
}
Aliaksandr Shpak
sumber
2

Solusi sementara

Anda dapat membuat async init() {... return this;}metode, lalu lakukan new MyClass().init()setiap kali Anda biasanya hanya mengatakan new MyClass().

Ini tidak bersih karena bergantung pada semua orang yang menggunakan kode Anda, dan Anda sendiri, untuk selalu instantiate objek seperti itu. Namun, jika Anda hanya menggunakan objek ini di satu atau dua tempat tertentu dalam kode Anda, itu mungkin baik-baik saja.

Masalah yang signifikan terjadi karena ES tidak memiliki sistem tipe, jadi jika Anda lupa menyebutnya, Anda baru saja kembali undefined karena konstruktor tidak mengembalikan apa pun. Ups. Jauh lebih baik melakukan sesuatu seperti:

Hal terbaik untuk dilakukan adalah:

class AsyncOnlyObject {
    constructor() {
    }
    async init() {
        this.someField = await this.calculateStuff();
    }

    async calculateStuff() {
        return 5;
    }
}

async function newAsync_AsyncOnlyObject() {
    return await new AsyncOnlyObject().init();
}

newAsync_AsyncOnlyObject().then(console.log);
// output: AsyncOnlyObject {someField: 5}

Solusi metode pabrik (sedikit lebih baik)

Namun kemudian Anda mungkin secara tidak sengaja melakukan AsyncOnlyObject baru, Anda mungkin harus membuat fungsi pabrik yang menggunakan Object.create(AsyncOnlyObject.prototype)secara langsung:

async function newAsync_AsyncOnlyObject() {
    return await Object.create(AsyncOnlyObject.prototype).init();
}

newAsync_AsyncOnlyObject().then(console.log);
// output: AsyncOnlyObject {someField: 5}

Namun katakanlah Anda ingin menggunakan pola ini pada banyak objek ... Anda bisa mengabstraksikan ini sebagai dekorator atau sesuatu yang Anda (secara verbal, ugh) panggil setelah mendefinisikan seperti postProcess_makeAsyncInit(AsyncOnlyObject), tapi di sini saya akan menggunakan extendskarena ini cocok dengan semantik subkelas. (subclass adalah kelas induk + tambahan, karena mereka harus mematuhi kontrak desain kelas induk, dan dapat melakukan hal-hal tambahan; subkelas async akan aneh jika induknya juga tidak async, karena tidak dapat diinisialisasi sama cara):


Solusi yang diabstraksi (versi extends / subclass)

class AsyncObject {
    constructor() {
        throw new Error('classes descended from AsyncObject must be initialized as (await) TheClassName.anew(), rather than new TheClassName()');
    }

    static async anew(...args) {
        var R = Object.create(this.prototype);
        R.init(...args);
        return R;
    }
}

class MyObject extends AsyncObject {
    async init(x, y=5) {
        this.x = x;
        this.y = y;
        // bonus: we need not return 'this'
    }
}

MyObject.anew('x').then(console.log);
// output: MyObject {x: "x", y: 5}

(jangan gunakan dalam produksi: Saya belum memikirkan skenario rumit seperti apakah ini cara yang tepat untuk menulis pembungkus untuk argumen kata kunci.)

ninjagecko
sumber
2

Tidak seperti yang dikatakan orang lain, Anda bisa membuatnya bekerja.

JavaScript classdapat mengembalikan apa pun secara harfiah dari mereka constructor, bahkan instance dari kelas lain. Jadi, Anda dapat mengembalikan a Promisedari konstruktor kelas Anda yang memutuskan ke instance aktualnya.

Di bawah ini adalah contohnya:

export class Foo {

    constructor() {

        return (async () => {

            // await anything you want

            return this; // Return the newly-created instance
        }).call(this);
    }
}

Kemudian, Anda akan membuat contoh Foodengan cara ini:

const foo = await new Foo();
Davide Cannizzo
sumber
1

Jika Anda dapat menghindari extend , Anda dapat menghindari semua kelas bersama-sama dan menggunakan komposisi fungsi sebagai konstruktor . Anda bisa menggunakan variabel dalam lingkup alih-alih anggota kelas:

async function buildA(...) {
  const data = await fetch(...);
  return {
    getData: function() {
      return data;
    }
  }
}

dan sederhana menggunakannya sebagai

const a = await buildA(...);

Jika Anda menggunakan naskah atau alur, Anda bahkan dapat menegakkan antarmuka konstruktor

Interface A {
  getData: object;
}

async function buildA0(...): Promise<A> { ... }
async function buildA1(...): Promise<A> { ... }
...
7ynk3r
sumber
0

Variasi pada pola pembuat, 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;
}
Jeff Lowery
sumber
0

Anda dapat segera menjalankan fungsi async anonim yang mengembalikan pesan dan mengaturnya ke variabel pesan. Anda mungkin ingin melihat ekspresi fungsi yang langsung dipanggil (IEFES), jika Anda tidak terbiasa dengan pola ini. Ini akan bekerja seperti pesona.

var message = (async function() { return await grabUID(uid) })()
Umesh KC
sumber
-1

@ slebetmen jawaban yang diterima menjelaskan dengan baik mengapa ini tidak berhasil. Selain dua pola yang disajikan dalam jawaban itu, opsi lain adalah hanya mengakses properti async Anda melalui pengambil async kustom. Konstruktor () kemudian dapat memicu pembuatan properti async, tetapi pengambil kemudian memeriksa untuk melihat apakah properti tersedia sebelum menggunakan atau mengembalikannya.

Pendekatan ini sangat berguna ketika Anda ingin menginisialisasi objek global sekali pada startup, dan Anda ingin melakukannya di dalam modul. Alih-alih menginisialisasi di Anda index.jsdan melewati contoh di tempat-tempat yang membutuhkannya, cukuprequire modul Anda di mana pun objek global diperlukan.

Pemakaian

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

Penerapan

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;
    }
  }
}
Neal
sumber
-2

Jawaban lainnya tidak jelas. Cukup panggil fungsi async dari konstruktor Anda:

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>
    `
}
Navigator
sumber
Seperti jawaban "jelas" lainnya di sini , jawaban ini tidak akan melakukan apa yang biasanya diharapkan oleh programmer dari konstruktor, yaitu konten diatur ketika objek dibuat.
Dan Dascalescu
2
@DanDascalescu Sudah diatur, hanya secara serempak, persis seperti yang diminta oleh si penanya. Maksud Anda adalah bahwa konten tidak disetel secara sinkron ketika objek dibuat, yang tidak diperlukan oleh pertanyaan. Itu sebabnya pertanyaannya adalah tentang menggunakan menunggu / async dari dalam konstruktor. Saya telah mendemonstrasikan bagaimana Anda dapat memanggil sebanyak menunggu / async yang Anda inginkan dari konstruktor dengan memanggil fungsi async dari itu. Saya telah menjawab pertanyaan dengan sempurna.
Navigateur
@ Navigator yang merupakan solusi yang sama dengan yang saya buat, tetapi komentar pada pertanyaan serupa lainnya menyarankan agar tidak dilakukan dengan cara ini. Masalah utama menjadi janji hilang di konstruktor, dan ini adalah antipattern. Apakah Anda memiliki referensi di mana ia merekomendasikan pendekatan memanggil fungsi async dari konstruktor Anda?
Marklar
1
@ Markark tidak ada referensi, mengapa Anda membutuhkannya? Tidak masalah jika ada sesuatu yang "hilang" jika Anda tidak membutuhkannya sejak awal. Dan jika Anda benar-benar membutuhkan janji, itu mudah untuk ditambahkan this.myPromise =(dalam arti umum) jadi bukan anti-pola dalam arti apa pun. Ada kasus-kasus yang benar-benar valid untuk perlu memulai algoritma async, pada konstruksi, yang tidak memiliki nilai balik itu sendiri, dan tetap menambahkan satu kita sederhana, jadi siapa pun yang menyarankan untuk tidak melakukan ini adalah salah paham sesuatu
Navigateur
1
Terima kasih telah meluangkan waktu untuk membalas. Saya mencari bacaan lebih lanjut karena jawaban yang bertentangan di sini di Stackoverflow. Saya berharap untuk mengkonfirmasi beberapa praktik terbaik untuk skenario ini.
Marklar
-2

Anda harus menambahkan thenfungsi ke instance. Promiseakan mengenalinya sebagai objek kemudian dengan Promise.resolvesecara 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)
}
吴浩锋
sumber
innerResolve(this)tidak akan bekerja, seperti thisyang masih layak. Ini mengarah pada resolusi rekursif yang tidak pernah berakhir.
Bergi