
غير متزامن هو زيادة معدل إشغال وحدة المعالجة المركزية وإبقائها مشغولة طوال الوقت.
بعض العمليات (الأكثر شيوعًا هي الإدخال/الإخراج) لا تتطلب مشاركة وحدة المعالجة المركزية وتستغرق وقتًا طويلاً للغاية. إذا لم يتم استخدام غير متزامن، فسوف تشكل حالة حظر، مما يتسبب في توقف وحدة المعالجة المركزية عن العمل وتجميد الصفحة.
عند حدوث عملية إدخال/إخراج في بيئة غير متزامنة، تضع وحدة المعالجة المركزية عمل الإدخال/الإخراج جانبًا (في هذا الوقت، يتم الاستيلاء على الإدخال/الإخراج بواسطة وحدات تحكم أخرى ولا يزال يتم نقل البيانات)، ثم تقوم بمعالجة المهمة التالية ، في انتظار اكتمال عملية الإدخال / الإخراج، قم بإخطار وحدة المعالجة المركزية (رد الاتصال هو طريقة إعلام) للعودة إلى العمل.
المحتوى الأساسي لـ "JavaScript غير المتزامن ورد الاتصال" هو أن وقت الانتهاء المحدد للعمل غير المتزامن غير مؤكد من أجل إجراء المعالجة اللاحقة بدقة بعد اكتمال العمل غير المتزامن، يجب تمرير رد الاتصال إلى الوظيفة غير المتزامنة. إكمال عملك، تابع المهام التالية.
على الرغم من أن عمليات الاسترجاعات يمكن أن تكون سهلة التنفيذ بشكل غير متزامن، إلا أنها يمكن أن تشكل جحيم رد الاتصال بسبب التداخل المتعدد. لتجنب جحيم رد الاتصال، تحتاج إلى إلغاء البرمجة المتداخلة وتغييرها إلى البرمجة الخطية.
Promise هو الحل الأفضل للتعامل مع رد الاتصال في JavaScript .
Promise "وعد". يمكننا تغليف العمل غير المتزامن ونطلق عليه Promise ، أي تقديم وعد ووعد بإعطاء إشارة واضحة بعد انتهاء العمل غير المتزامن!
بناء جملة Promise :
دع الوعد = وعد جديد(وظيفة(حل,رفض){
// عمل غير متزامن}) من خلال بناء الجملة أعلاه، يمكننا تغليف العمل غير المتزامن في Promise . الوظيفة التي تم تمريرها عند إنشاء Promise هي طريقة للتعامل مع العمل غير المتزامن، والمعروفة أيضًا باسم executor .
resolve reject هما دالتان رد اتصال توفرهما JavaScript نفسها ويمكن استدعاؤهما عندما يكمل executor المهمة:
resolve(result) - إذا تم إكمالها بنجاح، فسيتم إرجاع resultreject(error) - إذا فشل error و سيتم إنشاء error ؛سيتم تنفيذ executor تلقائيًا فور إنشاء Promise ، وستغير حالة التنفيذ الخاصة به حالة الخصائص الداخلية Promise :
state - pending في البداية، ثم يتم تحويلها إلى fulfilled بعد استدعاء resolve ، أو يصبح rejected عندما يتم استدعاء rejectresult undefined في البداية، ثم تصبح value بعد استدعاء resolve(value) ، أو تصبح error بعد استدعاء rejectfs.readFile وظيفة غير متزامنة؛ يمكننا تمريره في executor ويتم تنفيذ عمليات قراءة الملف في الملف، وبالتالي تغليف العمل غير المتزامن.
تحتوي التعليمة البرمجية التالية على وظيفة fs.readFile ، وتستخدم resolve(data) لمعالجة النتائج الناجحة reject(err) لمعالجة النتائج الفاشلة.
الكود كما يلي:
Let Promise = new Promise((الحل، الرفض) => {
fs.readFile('1.txt', (err, data) => {
console.log('قراءة 1.txt')
إذا (أخطأ) رفض (أخطأ)
حل (البيانات)
})}) إذا قمنا بتنفيذ هذا الكود، فسيتم إخراج الكلمات "قراءة 1.txt"، مما يثبت أن عملية قراءة الملف تتم مباشرة بعد إنشاء Promise .
عادةً ما يقوم
Promiseبتغليف التعليمات البرمجية غير المتزامنة داخليًا، ولكنه لا يقوم فقط بتغليف التعليمات البرمجية غير المتزامنة.
تتضمن حالة Promise أعلاه عملية قراءة الملف. سيتم قراءة الملف فورًا بعد اكتمال الإنشاء. إذا كنت ترغب في الحصول على نتيجة تنفيذ Promise ، فأنت بحاجة إلى استخدام ثلاث طرق then ، catch ، finally .
يمكن استخدام then Promise للتعامل مع العمل بعد اكتمال تنفيذ Promise ، حيث تتلقى معلمتين لرد الاتصال، ويكون بناء الجملة كما يلي:
Promise.then(function(result),function(error))
النتيجة
result المعلمة هي القيمة resolveerror reject؛ وعد = وعد جديد ((حل، رفض) => {
fs.readFile('1.txt', (err, data) => {
console.log('قراءة 1.txt')
إذا (أخطأ) رفض (أخطأ)
حل (البيانات)
})})promise.then(
(البيانات) => {
console.log("تم التنفيذ بنجاح، والنتيجة هي" + data.toString())
},
(يخطئ) => {
console.log ("فشل التنفيذ، الخطأ هو" + err.message)
}) إذا تم تنفيذ قراءة الملف بنجاح، فسيتم استدعاء الوظيفة الأولى:
PS E:CodeNodedemos 3-callback>node .index.js قراءة 1.txt إذا تم تنفيذها بنجاح، تكون النتيجة
حذف 1.txt إذا فشل التنفيذ، فسيتم استدعاء الوظيفة الثانية:
PS E:CodeNodedemos 3-callback>node .index.js. قراءة 1.txt فشل التنفيذ مع الخطأ ENOENT: لا يوجد مثل هذا الملف أو الدليل، افتح 'E:CodeNodedemos 3-callback1.txt'
إذا ركزنا فقط على نتيجة التنفيذ الناجح، فيمكننا تمرير ملف واحد فقط وظيفة رد الاتصال:
الوعد .ثم ((البيانات)=>{
console.log("تم التنفيذ بنجاح، والنتيجة هي" + data.toString())}) في هذه المرحلة قمنا بتنفيذ عملية قراءة غير متزامنة للملف.
إذا ركزنا فقط على نتيجة الفشل، فيمكننا تمرير null إلى النتيجة الأولى then رد الاتصال: promise.then(null,(err)=>{...}) .
أو استخدم طريقة أكثر أناقة: promise.catch((err)=>{...})
Let Promise = new Promise((resolve,reject) => {
fs.readFile('1.txt', (err, data) => {
console.log('قراءة 1.txt')
إذا (أخطأ) رفض (أخطأ)
حل (البيانات)
})})promise.catch((يخطئ)=>{
console.log(err.message)}) .catch((err)=>{...}) then(null,(err)=>{...}) لهما نفس التأثير تمامًا.
.finally هي دالة سيتم تنفيذها بغض النظر عن نتيجة promise ، ولها نفس الغرض الذي تستخدمه finally في try...catch... ، ويمكنها التعامل مع العمليات التي لا علاقة لها بالنتيجة.
على سبيل المثال:
وعد جديد((حل،رفض)=>{
//شيء ما...}).finally(()=>{console.log('تنفيذ بغض النظر عن النتيجة')}).then(result=>{...}, err=>{...} ) رد الاتصال أخيرًا لا يحتوي على معلمات، fs.readFile() 10 ملفات بالتسلسل وإخراج المحتويات من الملفات العشرة على التوالي.
نظرًا لأن fs.readFile() نفسه غير متزامن، فيجب علينا استخدام تداخل رد الاتصال كما يلي:
fs.readFile('1.txt', (err, data) => {.
console.log(data.toString()) //1
fs.readFile('2.txt', (err, data) => {
console.log(data.toString())
fs.readFile('3.txt', (err, data) => {
console.log(data.toString())
fs.readFile('4.txt', (err, data) => {
console.log(data.toString())
fs.readFile('5.txt', (err, data) => {
console.log(data.toString())
fs.readFile('6.txt', (err, data) => {
console.log(data.toString())
fs.readFile('7.txt', (err, data) => {
console.log(data.toString())
fs.readFile('8.txt', (err, data) => {
console.log(data.toString())
fs.readFile('9.txt', (err, data) => {
console.log(data.toString())
fs.readFile('10.txt', (err, data) => {
console.log(data.toString())
// ==> بوابة الجحيم})
})
})
})
})
})
})
})
})}) على الرغم من أن الكود أعلاه يمكنه إكمال المهمة، إلا أنه مع زيادة تداخل الاستدعاءات، يصبح مستوى الكود أعمق وتزداد صعوبة الصيانة، خاصة عندما نستخدم كودًا حقيقيًا قد يحتوي على العديد من الحلقات والعبارات الشرطية، بدلاً من console.log(...) في المثال.
إذا لم نستخدم عمليات الاسترجاعات وقمنا باستدعاء fs.readFile() مباشرة بالتسلسل وفقًا للتعليمة البرمجية التالية، فماذا سيحدث؟
// ملاحظة: هذه طريقة خاطئة لكتابة fs.readFile('1.txt', (err, data) => {
console.log(data.toString())})fs.readFile('2.txt', (err, data) => {
console.log(data.toString())})fs.readFile('3.txt', (err, data) => {
console.log(data.toString())})fs.readFile('4.txt', (err, data) => {
console.log(data.toString())})fs.readFile('5.txt', (err, data) => {
console.log(data.toString())})fs.readFile('6.txt', (err, data) => {
console.log(data.toString())})fs.readFile('7.txt', (err, data) => {
console.log(data.toString())})fs.readFile('8.txt', (err, data) => {
console.log(data.toString())})fs.readFile('9.txt', (err, data) => {
console.log(data.toString())})fs.readFile('10.txt', (err, data) => {
console.log(data.toString())}) فيما يلي نتائج الاختبار الذي أجريته (تختلف نتائج كل تنفيذ):
PS E:CodeNodedemos 3-callback> عقدة .indexالسبب أن
js12346957108
ينتج أن هذه النتيجة غير المتسلسلة غير متزامنة ، ولا يمكن تحقيق التوازي غير المتزامن في خيط واحد.
سبب استخدام حالة الخطأ هذه هنا هو التأكيد على مفهوم عدم التزامن. إذا لم تفهم سبب حدوث هذه النتيجة، فيجب عليك العودة وتعويض الدرس!
فكرة استخدام Promise لحل قراءة الملفات المتسلسلة غير المتزامنة:
promise1 واستخدام resolve لإرجاع النتيجة.promise1.then لتلقي وإخراج نتيجة قراءة الملف،promise2 الجديد في promise1.then ثم العودةpromise2.then ثم لتلقي وإخراج نتيجة القراءةpromise3 الجديد في promise2.then ثم العودةpromise3.then ثم لتلقي وإخراج نتيجة... الكود كما يلي:
دع الوعد1 = الوعد الجديد((الحل، الرفض) => {
fs.readFile('1.txt', (err, data) => {
إذا (أخطأ) رفض (أخطأ)
حل (البيانات)
})})دع الوعد2 = الوعد1.ثم(
البيانات => {
console.log(data.toString())
إرجاع وعد جديد ((حل، رفض) => {
fs.readFile('2.txt', (err, data) => {
إذا (أخطأ) رفض (أخطأ)
حل (البيانات)
})
})
})دع الوعد3 = الوعد2.ثم(
البيانات => {
console.log(data.toString())
إرجاع وعد جديد ((حل، رفض) => {
fs.readFile('3.txt', (err, data) => {
إذا (أخطأ) رفض (أخطأ)
حل (البيانات)
})
})
})دع الوعد4 = الوعد3.ثم(
البيانات => {
console.log(data.toString())
// .....
})... ... بهذه الطريقة نكتب جحيم رد الاتصال المتداخل الأصلي في الوضع الخطي.
ولكن لا تزال هناك مشكلة في الكود، على الرغم من أن الكود أصبح أكثر جمالا من حيث الإدارة، إلا أنه يزيد بشكل كبير من طول الكود.
الكود أعلاه طويل جدًا، يمكننا تقليل كمية الكود من خلال خطوتين:
promise الوسيط، وربط .thenالكود كما يلي:
الدالة myReadFile (path) {
إرجاع وعد جديد ((حل، رفض) => {
fs.readFile(path, (err, data) => {
إذا (أخطأ) رفض (أخطأ)
console.log(data.toString())
حل()
})
})}myReadFile('1.txt')
.then(data => { return myReadFile('2.txt') })
.then(data => { return myReadFile('3.txt') })
.then(data => { return myReadFile('4.txt') })
.then(data => { return myReadFile('5.txt') })
.then(data => { return myReadFile('6.txt') })
.then(data => { return myReadFile('7.txt') })
.then(data => { return myReadFile('8.txt') })
.then(data => { return myReadFile('9.txt') })
.then(data => { return myReadFile('10.txt') }) بما أن طريقة myReadFile ستُرجع Promise جديدًا، يمكننا تنفيذ طريقة .then مباشرة وتُسمى طريقة البرمجة هذه بالبرمجة المتسلسلة .
نتيجة تنفيذ التعليمات البرمجية هي كما يلي:
PS E:CodeNodedemos 3-callback> عقدة .index.js12345678910
هذا يكمل عملية قراءة الملف غير المتزامنة والمتسلسلة.
ملاحظة: يجب إرجاع كائن
Promiseجديد بالطريقة.thenلكل خطوة، وإلا فسيتم استلامPromiseالقديم السابق.وذلك لأن كل طريقة
thenستستمر في تمريرPromiseللأسفل.