Vous êtes ici car vous avez une question : comment attendre un constructeur de Classe ? Comment rendre le constructeur Async ???
Et la réponse, je suis désolé, est que vous ne pouvez littéralement pas. Un constructeur de classe en Javascript doit être synchrone. Cependant, il existe des alternatives d’implémentation qui peuvent vous aider à faire ce que vous voulez. Explorons-le !
Vous le souhaitez probablement parce que vous devez initialiser quelque chose dans le constructeur et le rendre disponible dans tout appel que vous effectuez à la classe. Un bon exemple serait de se connecter à une base de données et d'attendre que cette connexion soit établie avant d'accepter toute requête.
Le modèle est vraiment simple ! Commençons par un exemple de connecteur de base de données, utilisant le module sqlite sur npm :
import sqlite3 from 'sqlite3'; import { open } from 'sqlite';
Pour ouvrir cette base de données, vous devez attendre la fonction d'ouverture. J'ai demandé à mon sympathique assistant de code IA de créer une classe "DBConnector" et elle a en fait obtenu le bon modèle commun - celui que vous verrez partout :
class DBConnector { constructor() { this.db = null; } async connect() { this.db = await open({ filename: './database.sqlite', driver: sqlite3.Database, }); } async disconnect() { await this.db.close(); } async query(sql, params) { return this.db.all(sql, params); } } // exporting a singleton so there's only one connection export default new DBConnector();
Vous appelleriez cela simplement en l'important puis en attendant ses appels :
import db from './dbconnector.js'; await db.connect(); await db.query('GET * FROM test');
Alors maintenant, le problème ici, bien sûr, est que non seulement vous devez appeler manuellement myDB.connect() afin de démarrer la connexion, mais vous ne pouvez pas non plus garantir que l'appel de requête fonctionnera, par exemple, si un autre fichier effectue une requête pendant que votre fichier principal se connecte.
Bien sûr, le fichier principal peut attendre db.connect(); , mais tout ce qui importe ce module n'aura aucun moyen de le faire. Et vous pourriez penser : « ok mais je peux aussi appeler wait db.connect(); dans d'autres fichiers, n'est-ce pas ? et vous pouvez... mais cela vous reconnectera à la base de données à chaque fois, ce qui peut être lent selon ce que vous utilisez.
Le modèle que j'ai proposé implique un peu plus de complexité, mais il reste simple et garantit que chaque petit morceau de code - et vous-même - êtes tous satisfaits. Et je l'ai en fait inventé par moi-même, même si d'autres le savent. On les appelle promesses « différées ».
Voici comment cela fonctionne.
// still have our imports import sqlite3 from 'sqlite3'; import { open } from 'sqlite'; // Create the class class DBConnector { // We'll need these private properties: #db; #defer; #resolve; #reject; // Then we make our constructor: constructor() { // We create a new promise and store its resolve and reject // functions in the class instance properties. this.#defer = new Promise((res, rej) => { // this is the magic, right here, basically. this.#resolve = res; this.#reject = rej; }); // So now, this.#defer is a promise! We can await it in other methods. // Now we call our this.connect *internally* and automatically. this.connect(); } async connect() { try { this.#db = await open({ filename: `./database.sqlite`, driver: sqlite3.Database, }); // Now that we resolve the promise, any other method that awaits // this.#defer will continue executing. this.#resolve(); } catch (err) { // in case of error we can of course reject the promise // any code using it would then throw an error. this.#reject(err); } } async disconnect() { // on any action, we just await this.#defer await this.#defer; await this.#db.close(); } async query(sql, params) { // Even in queries, it works perfectly fine! await this.#defer; // Here we KNOW that this.#db is already initialized. return this.#db.all(sql, params); } } export default new DBConnector();
N'est-ce pas un charmant petit motif ? Il s'agit bien sûr plus de code qu'un exemple de base, mais personnellement, je pense que cela place définitivement la barre plus haut en matière de code passe-partout pour les classes qui sont principalement basées sur des méthodes asynchrones.
Voyons comment l'utiliser !
import db from './dbconnector.js'; // it's already initialized... just use it! await db.query('GET * FROM test'); // and it just works :D
J'utilise ce modèle si souvent qu'à la fin, j'ai décidé de créer une bibliothèque vraiment rapide et sale et de la publier sur NPM. Cela s'appelle des reports et c'est assez simple à utiliser (c'était aussi assez simple à écrire, une fois que j'ai connu le modèle).
Refaisons l'exemple ci-dessus en utilisant plutôt des reports.
import sqlite3 from 'sqlite3'; import { open } from 'sqlite';
La chaîne 'db' est essentiellement n'importe quel nom de chaîne que vous souhaitez donner au différé, et vous pouvez en créer autant que vous le souhaitez. Évidemment, vous n'avez pas besoin de cette bibliothèque, mais personnellement, je la trouve plutôt sympa, vous savez ?
J'espère que vous avez appris un modèle vraiment utile ici, et je suis heureux d'avoir fait partie de votre parcours d'apprentissage aujourd'hui <3
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!