Il existe une petite mais assez importante différence entre une fonction qui renvoie simplement une promesse et une fonction qui a été déclarée à l'aide du
async
.
Jetez un œil à l'extrait de code suivant:
function fn(obj) { const someProp = obj.someProp return Promise.resolve(someProp) } async function asyncFn(obj) { const someProp = obj.someProp return Promise.resolve(someProp) } asyncFn().catch(err => console.error('Catched'))
Comme vous pouvez le voir, les deux fonctions ont le même corps dans lequel nous essayons d'accéder à la propriété d'un argument qui n'est pas défini dans les deux cas. La seule différence entre les deux fonctions est que
asyncFn
déclaré à l'aide du
async
.
Cela signifie que JavaScript garantit que la fonction
asnycFn
renvoie une promesse (réussit ou échoue), même si une erreur s'y produit, dans notre cas, le bloc
.catch()
l'attrapera.
Cependant, dans le cas de la fonction
fn
, le moteur ne sait toujours pas que la fonction renverra une promesse, et donc le code n'atteindra pas le bloc
.catch()
, l'erreur ne sera pas interceptée et tombera sur la console.
Plus d'exemples de vie
Je sais ce que tu penses maintenant:
"Quand diable vais-je faire une telle erreur?"
Deviné?
Eh bien, créons une application simple qui fait exactement cela.
Supposons que nous ayons une application construite en utilisant Express et MongoDB qui utilise le pilote MongoDB Node.JS. Si vous ne me faites pas confiance, j'ai placé tout le code source dans
ce référentiel Github , vous pouvez donc le cloner et l'exécuter localement, mais je dupliquerai également tout le code ici.
Voici notre fichier
app.js
:
Jetez un œil au bloc
.catch()
! C'est là que la magie se produira (ne se produira pas).
Le fichier
db.js
utilisé pour se connecter à la base de données mongo:
'use strict' const MongoClient = require('mongodb').MongoClient const url = 'mongodb://localhost:27017' const dbName = 'async-promise-test' const client = new MongoClient(url) let db module.exports = { connect() { return new Promise((resolve, reject) => { client.connect(err => { if (err) return reject(err) console.log('Connected successfully to server') db = client.db(dbName) resolve(db) }) }) }, getDb() { return db } }
Et enfin, nous avons un fichier
user-model.js
dans lequel une
getUserById
fonction
getUserById
est actuellement définie:
Si vous regardez à
app.js
fichier
app.js
, vous verrez que lorsque nous allons sur
localhost:3000/users/<id>
nous appelons la fonction
getUserById
définie dans le
user-model.js
, en passant le paramètre
id
comme demande.
Disons que vous allez à l'adresse suivante:
localhost:3000/users/1
. Que pensez-vous qu'il va se passer ensuite?
Eh bien, si vous avez répondu: «Je verrai une énorme erreur de MongoClient» - vous aviez raison. Pour être plus précis, vous verrez l'erreur suivante:
Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
.
Et pensez-vous que le bloc
.catch()
sera appelé dans l'extrait de code suivant?
Non. Il ne sera pas appelé.
Et que se passe-t-il si vous changez une déclaration de fonction en ceci?
module.exports = {
Oui, vous commencez à comprendre ce qui est quoi. Notre bloc
.catch()
sera appelé et nous pourrons traiter l'erreur interceptée et la montrer à l'utilisateur.
Au lieu d'une conclusion
J'espère que ces informations ont été utiles à certains d'entre vous. Veuillez noter que cet article n'essaie pas de vous forcer à toujours utiliser des fonctions asynchrones - bien qu'elles soient plutôt cool. Ils ont leurs propres cas d'utilisation, mais ils sont toujours du sucre syntaxique par rapport aux promesses.
Je voulais juste que vous sachiez que parfois les promesses peuvent faire une grande différence, et quand (oui, pas «si») vous rencontrez l'erreur discutée dans cet article, vous saurez la raison possible de son occurrence.
Remarque PS perev.: à l'article d'origine, un commentaire utile a été laissé par l'utilisateur Craig P Hicks, que (après commentaires dans les commentaires) j'ai décidé de citer ici:Je voudrais attirer l'attention sur un détail (dans mon environnement de développement), les erreurs qui se produisent dans le corps de Promise.resolve({<body>})
ne Promise.resolve({<body>})
pas "détectées":
Promise.resolve((()=>{throw "oops"; })()) .catch(e=>console("Catched ",e));
mais les erreurs qui se produisent dans le corps de la new Promise()
( traduction approximative: dans la «promesse appropriée» d'origine ) sont «détectées»:
(new Promise((resolve,reject)=>{ resolve((()=>{throw "oops"})()) })) .catch(e=>console.log("Catched ",e));
Que diriez-vous de cette déclaration:
async function fn() { <body> }
sémantiquement, cette option est équivalente à ceci:
function fn() { return new Promise((resolve,reject)=>{ resolve({ <body> }) }) }
Par conséquent, le fragment de code ci-dessous détectera des erreurs si le <body> a une new Promise()
( environ. Trad.: Dans la «promesse appropriée» d'origine ):
function fn() { return Promise.resolve({<body}); }
Ainsi, pour que l'exemple du début de l'article "capture" les erreurs dans les deux cas, il est nécessaire de renvoyer non pas Promise.resolve()
, mais de new Promise()
dans les fonctions: function fn(obj) { return new Promise((resolve, reject) => { const someProp = obj.someProp; resolve(someProp); }); } async function asyncFn(obj) { return new Promise((resolve, reject) => { const someProp = obj.someProp; resolve(someProp); }); } asyncFn().catch(err => console.error("Catched"));