Saat ini, saya mencoba untuk menggunakan async/await
fungsi konstruktor kelas. Ini agar saya bisa mendapatkan e-mail
tag 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 ()?
javascript
node.js
async-await
Alexander Craggs
sumber
sumber
<e-mail data-uid="1028"></email>
dan dari sana diisi dengan informasi menggunakancustomElements.define()
metode..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.Jawaban:
Ini tidak akan pernah berhasil.
Kata
async
kunci memungkinkanawait
untuk digunakan dalam fungsi yang ditandai sebagaiasync
tetapi juga mengubah fungsi itu menjadi generator janji. Jadi fungsi yang ditandai denganasync
akan 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.
Penggunaan suatu
init()
fungsi. Ini bekerja sedikit seperti jQuery.ready()
. Objek yang Anda buat hanya dapat digunakan di dalamnyainit
atauready
berfungsi:Pemakaian:
Penerapan:
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:
Penerapan:
Implementasi dengan async / menunggu:
Catatan tentang fungsi panggilan di dalam fungsi statis.
Ini tidak ada hubungannya dengan konstruktor async tetapi dengan apa arti kata kunci
this
sebenarnya (yang mungkin sedikit mengejutkan bagi orang-orang yang berasal dari bahasa yang melakukan resolusi otomatis dari nama metode, yaitu, bahasa yang tidak memerlukanthis
kata kunci).Kata
this
kunci 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:
Anda tidak dapat melakukan:
alih-alih, Anda perlu menyebutnya sebagai:
Karenanya, kode berikut akan menghasilkan kesalahan:
Untuk memperbaikinya, Anda dapat membuat
bar
fungsi biasa atau metode statis:sumber
init()
tetapi memiliki fungsi yang dikaitkan dengan beberapa atribut tertentu sepertisrc
atauhref
(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)bind
diperlukan dalam contoh pertamacallback.bind(this)();
? Sehingga Anda bisa melakukan hal-hal sepertithis.otherFunc()
di dalam callback?this
mengacu pada callbackmyClass
. Jika Anda selalu menggunakanmyObj
bukannyathis
Anda tidak membutuhkannyaconst a = await new A()
dengan cara yang sama kami memiliki fungsi reguler dan fungsi async.Anda pasti bisa melakukan ini. Pada dasarnya:
untuk membuat penggunaan kelas:
Solusi ini memiliki beberapa jatuh pendek:
sumber
constructor(x) { return (async()=>{await f(x); return this})() }
return this
diperlukan, karena ketikaconstructor
melakukannya secara otomatis untuk Anda, async IIFE itu tidak, dan Anda akhirnya akan mengembalikan objek kosong.T
harus kembaliT
ketika dibangun tetapi untuk mendapatkan kemampuan async kita mengembalikanPromise<T>
yang memutuskanthis
, 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 ituKarena fungsi async adalah janji, Anda bisa membuat fungsi statis di kelas Anda yang mengeksekusi fungsi async yang mengembalikan instance kelas:
Panggilan dengan
let yql = await Yql.init()
dari fungsi async.sumber
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:
ini memulai muatan asinkron aset sumber yang, ketika berhasil, berakhir
onload
dan ketika salah, berakhironerror
. Jadi, buat kelas Anda sendiri melakukan ini juga:Dan kemudian Anda membuat fungsi renderLoaded / renderError berurusan dengan panggilan acara dan dom bayangan:
Perhatikan juga bahwa saya mengubah Anda
id
menjadi aclass
, 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.sumber
Saya membuat test case ini berdasarkan jawaban @ Downgoat.
Ini berjalan pada NodeJS. Ini adalah kode Downgoat di mana bagian async disediakan oleh
setTimeout()
panggilan.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:
untuk memiliki properti yang tersedia suka
cook.getDisplayName()
.Dengan solusi ini yang harus saya lakukan:
yang sangat mirip dengan yang ideal.
Juga, saya perlu melakukan ini di dalam sebuah
async
fungsi.B-plan saya adalah membiarkan data keluar dari konstruktor, berdasarkan saran @slebetman untuk menggunakan fungsi init, dan melakukan sesuatu seperti ini:
yang tidak melanggar aturan.
sumber
menggunakan metode async dalam membangun ???
sumber
Solusi sementara
Anda dapat membuat
async init() {... return this;}
metode, lalu lakukannew MyClass().init()
setiap kali Anda biasanya hanya mengatakannew 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:
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: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 menggunakanextends
karena 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)
(jangan gunakan dalam produksi: Saya belum memikirkan skenario rumit seperti apakah ini cara yang tepat untuk menulis pembungkus untuk argumen kata kunci.)
sumber
Tidak seperti yang dikatakan orang lain, Anda bisa membuatnya bekerja.
JavaScript
class
dapat mengembalikan apa pun secara harfiah dari merekaconstructor
, bahkan instance dari kelas lain. Jadi, Anda dapat mengembalikan aPromise
dari konstruktor kelas Anda yang memutuskan ke instance aktualnya.Di bawah ini adalah contohnya:
Kemudian, Anda akan membuat contoh
Foo
dengan cara ini:sumber
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:dan sederhana menggunakannya sebagai
Jika Anda menggunakan naskah atau alur, Anda bahkan dapat menegakkan antarmuka konstruktor
sumber
Variasi pada pola pembuat, menggunakan panggilan ():
sumber
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.
sumber
@ 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.js
dan melewati contoh di tempat-tempat yang membutuhkannya, cukuprequire
modul Anda di mana pun objek global diperlukan.Pemakaian
Penerapan
sumber
Jawaban lainnya tidak jelas. Cukup panggil fungsi async dari konstruktor Anda:
sumber
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 sesuatuAnda harus menambahkan
then
fungsi ke instance.Promise
akan mengenalinya sebagai objek kemudian denganPromise.resolve
secara otomatissumber
innerResolve(this)
tidak akan bekerja, sepertithis
yang masih layak. Ini mengarah pada resolusi rekursif yang tidak pernah berakhir.