Es gibt einen kleinen, aber wichtigen Unterschied zwischen einer Funktion, die einfach ein Versprechen zurückgibt, und einer Funktion, die mit dem
async
.
Schauen Sie sich das folgende Code-Snippet an:
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'))
Wie Sie sehen, haben beide Funktionen denselben Hauptteil, in dem versucht wird, auf die Eigenschaft eines Arguments zuzugreifen, das in beiden Fällen nicht definiert ist. Der einzige Unterschied zwischen den beiden Funktionen besteht darin, dass
asyncFn
mit dem
async
.
Dies bedeutet, dass JavaScript sicherstellt, dass die
asnycFn
Funktion ein Versprechen zurückgibt (entweder erfolgreich oder
asnycFn
), auch wenn ein Fehler darin auftritt. In unserem Fall wird dies vom
.catch()
-Block
.catch()
.
Im Fall der Funktion
fn
weiß die Engine jedoch immer noch nicht, dass die Funktion ein Versprechen
.catch()
, und daher erreicht der Code den Block
.catch()
, der Fehler wird nicht abgefangen und fällt auf die Konsole.
Mehr Lebensbeispiel
Ich weiß, was Sie jetzt denken:
"Wann zum Teufel mache ich so einen Fehler?"
Erraten?
Nun, lassen Sie uns eine einfache Anwendung erstellen, die genau das tut.
Angenommen, wir haben eine Anwendung, die mit Express und MongoDB erstellt wurde und den MongoDB Node.JS-Treiber verwendet. Wenn Sie mir nicht vertrauen, habe ich den gesamten Quellcode in
dieses Github-Repository gestellt , damit Sie ihn klonen und lokal ausführen können. Ich werde jedoch auch den gesamten Code hier duplizieren.
Hier ist unsere
app.js
Datei:
Schauen Sie sich den
.catch()
-Block genau an! Hier wird (wird) keine Magie auftreten.
Die Datei
db.js
verwendet, um eine Verbindung zur Mongo-Datenbank
db.js
:
'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 } }
Und schließlich haben wir eine
user-model.js
Datei, in der derzeit nur eine
getUserById
Funktion definiert ist:
Wenn Sie sich die Datei
app.js
, werden Sie
app.js
, dass wir beim
app.js
von
localhost:3000/users/<id>
die in der
user-model.js
definierte Funktion
getUserById
user-model.js
und den Parameter
id
als Anforderung übergeben.
Angenommen, Sie gehen zu folgender Adresse:
localhost:3000/users/1
. Was wird wohl als nächstes passieren?
Nun, wenn Sie antworteten: "Ich werde einen großen Fehler von MongoClient sehen" - Sie hatten Recht. Genauer gesagt wird der folgende Fehler
Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
:
Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
.
Und glaubst du, der
.catch()
-Block wird im nächsten Code-Snippet aufgerufen?
Nein. Er wird nicht angerufen.
Und was passiert, wenn Sie eine Funktionsdeklaration in diese ändern?
module.exports = {
Ja, du fängst an zu verstehen, was was ist. Unser
.catch()
-Block wird aufgerufen, und wir können den festgestellten Fehler verarbeiten und dem Benutzer
.catch()
.
Anstelle einer Schlussfolgerung
Ich hoffe, diese Informationen haben einigen von Ihnen geholfen. Bitte beachten Sie, dass dieser Artikel Sie nicht zwingen soll, immer asynchrone Funktionen zu verwenden - obwohl diese ziemlich cool sind. Sie haben ihre eigenen Anwendungsfälle, aber sie sind immer noch syntaktischer Zucker über Versprechungen.
Ich wollte nur, dass Sie wissen, dass Versprechen manchmal einen großen Unterschied machen können, und wenn (ja, nicht „wenn“) Sie auf den in diesem Artikel beschriebenen Fehler stoßen, werden Sie den möglichen Grund für das Auftreten kennen.
PS Hinweis perev.: Zum Originalartikel wurde ein nützlicher Kommentar von dem Benutzer Craig P Hicks hinterlassen, den ich (nach Kommentaren in den Kommentaren) hier zitiert habe:Ich möchte auf ein Detail Promise.resolve({<body>})
, dass (in meiner Entwicklungsumgebung) Fehler, die im Body von Promise.resolve({<body>})
nicht "abgefangen" werden:
Promise.resolve((()=>{throw "oops"; })()) .catch(e=>console("Catched ",e));
Aber Fehler, die im Hauptteil von new Promise()
( ungefähr übersetzt: im ursprünglichen "richtigen Versprechen" ), werden "abgefangen":
(new Promise((resolve,reject)=>{ resolve((()=>{throw "oops"})()) })) .catch(e=>console.log("Catched ",e));
Wie wäre es mit dieser Aussage:
async function fn() { <body> }
semantisch ist diese Option gleichbedeutend mit:
function fn() { return new Promise((resolve,reject)=>{ resolve({ <body> }) }) }
Folglich wird das folgende Codefragment Fehler abfangen, wenn der <body> ein new Promise()
( ungefähr übersetzt: im ursprünglichen "richtigen Versprechen" ):
function fn() { return Promise.resolve({<body}); }
Damit das Beispiel vom Anfang des Artikels in beiden Fällen Fehler „ Promise.resolve()
“, muss nicht Promise.resolve()
, sondern new Promise()
in den Funktionen zurückgegeben werden: 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"));