يوجد اختلاف بسيط ولكنه مهم بين دالة تُرجع ببساطة وعد ووظيفة تم إعلانها باستخدام
async
.
ألقِ نظرة على مقتطف الشفرة التالي:
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'))
كما ترى ، فإن كلتا الوظيفتين لها نفس الجسم الذي نحاول فيه الوصول إلى خاصية وسيطة غير محددة في كلتا الحالتين. الاختلاف الوحيد بين الدالتين هو أن
asyncFn
إعلانه باستخدام
async
.
هذا يعني أن JavaScript يضمن أن وظيفة
asnycFn
تُرجع وعدًا (إما ينجح أو يفشل) ، حتى لو حدث خطأ فيه ، في حالتنا فإن كتلة
.catch()
به.
ومع ذلك ، في حالة دالة
fn
، لا يعرف
.catch()
بعد أن الوظيفة ستعود بوعد ، وبالتالي لن تصل الشفرة إلى كتلة
.catch()
، ولن يتم اكتشاف الخطأ
.catch()
في وحدة التحكم.
مثال عن الحياة
أنا أعرف ما تفكر به الآن:
"متى بحق الجحيم سأرتكب مثل هذا الخطأ؟"
خمنت؟
حسنًا ، دعنا ننشئ تطبيقًا بسيطًا يفعل ذلك.
افترض أن لدينا تطبيقًا تم إنشاؤه باستخدام Express و MongoDB يستخدم برنامج التشغيل MongoDB Node.JS. إذا كنت لا تثق بي ، فقد وضعت كل شفرة المصدر في
مستودع جيثب ، بحيث يمكنك استنساخها وتشغيلها محليًا ، لكنني سأكرر أيضًا جميع الشفرات هنا.
هنا هو ملف
app.js
لدينا:
نلقي نظرة فاحصة على كتلة
.catch()
! هذا هو المكان السحري (لن) يحدث.
db.js
استخدام ملف
db.js
للاتصال بقاعدة بيانات 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 } }
وأخيرًا ، لدينا ملف
user-model.js
فيه
getUserById
وظيفة
getUserById
واحدة فقط:
إذا نظرت إلى ملف
app.js
، فسترى ذلك عندما نذهب إلى
localhost:3000/users/<id>
getUserById
localhost:3000/users/<id>
نسميها الدالة
getUserById
المحددة في
user-model.js
، لتمرير معلمة
id
user-model.js
.
لنفترض أنك انتقلت إلى العنوان التالي:
localhost:3000/users/1
. ما رأيك سيحدث بعد ذلك؟
حسنًا ، إذا أجبت: "سوف أرى خطأً كبيرًا من MongoClient" - لقد كنت على حق. لكي تكون أكثر دقة ، سترى الخطأ التالي:
Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
.
هل تعتقد أنه سيتم استدعاء كتلة
.catch()
في مقتطف الرمز التالي؟
لا. لن يتم استدعاؤه.
وماذا يحدث إذا قمت بتغيير إعلان دالة إلى هذا؟
module.exports = {
نعم ، عليك أن تبدأ في فهم ما هو ما. سيتم استدعاء كتلة
.catch()
، وسنكون قادرين على معالجة الخطأ الذي تم اكتشافه
.catch()
للمستخدم.
بدلا من الاستنتاج
آمل أن تكون هذه المعلومات مفيدة لبعضكم. يرجى ملاحظة أن هذه المقالة لا تحاول إجبارك على استخدام وظائف غير متزامنة دائمًا - رغم أنها رائعة. لديهم حالات الاستخدام الخاصة بهم ، لكنها لا تزال السكر النحوي على الوعود.
أردت فقط أن تعرف أنه في بعض الأحيان يمكن أن تحدث الوعود فرقًا كبيرًا ، وعندما تصادف الخطأ الذي تمت مناقشته في هذه المقالة (نعم ، لا "إذا") ، ستعرف السبب المحتمل لحدوثه.
ملاحظة PS perev.: على المقال الأصلي ، تم ترك تعليق مفيد من المستخدم Craig P Hicks ، والذي (بعد التعليقات في التعليقات) قررت أن أقتبس هنا:أرغب في لفت الانتباه إلى أحد التفاصيل ، (في بيئة التطوير الخاصة بي) ، الأخطاء التي تحدث في نص Promise.resolve({<body>})
لا Promise.resolve({<body>})
" Promise.resolve({<body>})
":
Promise.resolve((()=>{throw "oops"; })()) .catch(e=>console("Catched ",e));
لكن الأخطاء التي تحدث في نص new Promise()
( تقريبًا. الترجمة: في "الوعد المناسب" الأصلي " يتم اكتشافها":
(new Promise((resolve,reject)=>{ resolve((()=>{throw "oops"})()) })) .catch(e=>console.log("Catched ",e));
ماذا عن هذا البيان:
async function fn() { <body> }
بمعنى أن هذا الخيار يعادل هذا:
function fn() { return new Promise((resolve,reject)=>{ resolve({ <body> }) }) }
وبالتالي ، فإن جزء الشفرة أدناه سيصطدم بالأخطاء إذا كان <body> لديه new Promise()
( تقريبًا. ترجم.: في "الوعد الأصلي" الأصلي:)
function fn() { return Promise.resolve({<body}); }
وبالتالي ، من أجل المثال من بداية المقالة إلى "التقاط" الأخطاء في كلتا الحالتين ، من الضروري إرجاع ليس Promise.resolve()
، ولكن new Promise()
في الوظائف: 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"));