Perbedaan antara fungsi asinkron dan fungsi yang mengembalikan janji

Ada perbedaan kecil namun agak penting antara fungsi yang hanya mengembalikan janji dan fungsi yang dinyatakan menggunakan async .

Lihatlah potongan kode berikut:

 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 

Seperti yang Anda lihat, kedua fungsi memiliki badan yang sama di mana kami mencoba mengakses properti argumen yang tidak didefinisikan dalam kedua kasus. Satu-satunya perbedaan antara kedua fungsi adalah asyncFn dideklarasikan menggunakan async .

Ini berarti bahwa JavaScript memastikan bahwa fungsi asnycFn mengembalikan janji (berhasil atau gagal), bahkan jika kesalahan terjadi di dalamnya, dalam kasus kami, blok .catch() akan menangkapnya.

Namun, dalam kasus fungsi fn , mesin masih tidak tahu bahwa fungsi akan mengembalikan janji, dan oleh karena itu kode tidak akan mencapai blok .catch() , kesalahan tidak akan tertangkap dan akan jatuh ke konsol.

Lebih banyak contoh hidup


Saya tahu apa yang Anda pikirkan sekarang:
"Kapan aku akan membuat kesalahan seperti itu?"

Tertebak?

Baiklah, mari kita buat aplikasi sederhana yang melakukan hal itu.

Misalkan kita memiliki aplikasi yang dibangun menggunakan Express dan MongoDB yang menggunakan driver MongoDB Node.JS. Jika Anda tidak mempercayai saya, saya telah menempatkan semua kode sumber di repositori Github ini , sehingga Anda dapat mengkloningnya dan menjalankannya secara lokal, tetapi saya juga akan menggandakan semua kode di sini.

Ini file app.js kami:

 // 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')) 

Lihatlah dengan cermat pada blok .catch() ! Di sinilah keajaiban tidak akan terjadi.

File db.js digunakan untuk terhubung ke database 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 } } 

Dan akhirnya, kami memiliki file user-model.js di mana hanya satu fungsi getUserById yang saat ini didefinisikan:

 // 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) }) } } 

Jika Anda melihat file app.js , Anda akan melihat bahwa ketika kita pergi ke localhost:3000/users/<id> kita memanggil fungsi getUserById didefinisikan dalam file user-model.js , melewati parameter id sebagai permintaan.

Katakanlah Anda pergi ke alamat berikut: localhost:3000/users/1 . Menurut Anda apa yang akan terjadi selanjutnya?

Nah, jika Anda menjawab: "Saya akan melihat kesalahan besar dari MongoClient" - Anda benar. Untuk lebih tepatnya, Anda akan melihat kesalahan berikut: Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters .

Dan menurut Anda apakah blok .catch() akan dipanggil dalam cuplikan kode berikutnya?

 // 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' })) // <===  ! }) // ...  ... 

Tidak. Dia tidak akan dipanggil.

Dan apa yang terjadi jika Anda mengubah deklarasi fungsi untuk ini?

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

Ya, Anda mulai mengerti apa itu. .catch() akan dipanggil, dan kami akan dapat memproses kesalahan yang tertangkap dan menunjukkannya kepada pengguna.

Alih-alih sebuah kesimpulan


Saya harap informasi ini bermanfaat bagi sebagian dari Anda. Harap perhatikan bahwa artikel ini tidak mencoba memaksa Anda untuk selalu menggunakan fungsi asinkron - meskipun sangat keren. Mereka memiliki kasus penggunaan sendiri, tetapi mereka masih sintaksis atas janji.

Saya hanya ingin Anda tahu bahwa kadang-kadang janji bisa membuat perbedaan besar, dan ketika (ya, bukan "jika") Anda menemukan kesalahan yang dibahas dalam artikel ini, Anda akan tahu alasan yang mungkin untuk terjadinya.

Catatan PS perev.: ke artikel asli, komentar yang berguna ditinggalkan dari pengguna Craig P Hicks, yang (setelah komentar di komentar) saya memutuskan untuk mengutip di sini:
Saya ingin menarik perhatian pada satu detail, (di lingkungan pengembangan saya) kesalahan yang terjadi di tubuh Promise.resolve({<body>}) tidak "tertangkap":

 Promise.resolve((()=>{throw "oops"; })()) .catch(e=>console("Catched ",e)); //  .catch()      "" 

tetapi kesalahan yang terjadi di tubuh new Promise() ( sekitar terjemahan: dalam "Janji yang tepat" yang asli ) adalah "tertangkap":

 (new Promise((resolve,reject)=>{ resolve((()=>{throw "oops"})()) })) .catch(e=>console.log("Catched ",e)); // Catched oops 

Bagaimana dengan pernyataan ini:

 async function fn() { <body> } 

semantik, opsi ini setara dengan ini:

 function fn() { return new Promise((resolve,reject)=>{ resolve({ <body> }) }) } 

Karenanya, fragmen kode di bawah ini akan menemukan kesalahan jika <body> memiliki new Promise() ( sekitar Terjemahan: dalam "Janji yang tepat" ):

 function fn() { return Promise.resolve({<body}); } 

Jadi, agar contoh dari awal artikel untuk "menangkap" kesalahan dalam kedua kasus, perlu untuk mengembalikan bukan Promise.resolve() , tetapi new Promise() dalam fungsi:

 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/id475260/


All Articles