Der Unterschied zwischen einer asynchronen Funktion und einer Funktion, die ein Versprechen zurückgibt

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')) // => 'Catched' fn().catch(err => console.error('Catched')) // => TypeError: Cannot read property 'someProp' of undefined 

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:

 // app.js 'use strict' const express = require('express') const db = require('./db') const userModel = require('./models/user-model') const app = express() db.connect() app.get('/users/:id', (req, res) => { return userModel .getUserById(req.params.id) .then(user => res.json(user)) .catch(err => res.status(400).json({ error: 'An error occured' })) // <===  ! }) app.listen(3000, () => console.log('Server is listening')) 

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:

 // models/user-model.js 'use strict' const ObjectId = require('mongodb').ObjectId const db = require('../db') const collectionName = 'users' module.exports = { /** * Get's a user by it's ID * @param {string} id The id of the user * @returns {Promise<Object>} The user object */ getUserById(id) { return db .getDb() .collection(collectionName) .findOne({ _id: new ObjectId(id) }) } } 

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?

 // app.js // ...  ... app.get('/users/:id', (req, res) => { return userModel .getUserById(req.params.id) .then(user => res.json(user)) .catch(err => res.status(400).json({ error: 'An error occured' })) // <===  ! }) // ...  ... 

Nein. Er wird nicht angerufen.

Und was passiert, wenn Sie eine Funktionsdeklaration in diese ändern?

 module.exports = { //  ,    async    ! async findById(id) { return db .getDb() .collection(collectionName) .findOne({ _id: new ObjectId(id) }) } } 

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)); //  .catch()      "" 

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)); // Catched oops 

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")); // => 'Catched' fn().catch(err => console.error("Catched")); // => 'Catched' 

Source: https://habr.com/ru/post/de475260/


All Articles