پاسخ به تماس ها برای مدیریت کدهای ناهمزمان در جاوا اسکریپت خوب عمل می کند، اما وعده ها و کلمات کلیدی همگام و انتظار پاک تر و انعطاف پذیرتر هستند.
برخورد با کد ناهمزمان – به معنای هر نوع کدی که بلافاصله اجرا نمی شود – می تواند مشکل باشد. رفتار ناهمزمان یکی از منابع اصلی پیچیدگی در هر محیط نرم افزاری است. نشان دهنده وقفه ای در جریان اجراست که ممکن است به هر تعداد نتیجه منجر شود. این مقاله سه راه برای مدیریت رفتار ناهمزمان در جاوا اسکریپت را معرفی می کند. ما با نگاهی به تماسها شروع میکنیم، سپس وعدهها و کلیدواژههای async
و await
را بهعنوان جایگزینهای مدرنتر معرفی میکنم.
کد ناهمزمان چیست؟
کد ناهمزمان (کد ناهمگام) میگوید: برو در حالی که کارهای دیگر را انجام میدهم، کاری انجام بده، سپس وقتی نتایج آماده شد به من اطلاع بده که چه اتفاقی افتاده است. همچنین به عنوان همزمان شناخته می شود، async در انواع موارد استفاده مهم است، اما به ویژه در هنگام تعامل با سیستم های خارجی و شبکه حیاتی است. در مرورگر، برقراری تماسهای API از راه دور یک مورد استفاده همهجا برای کدهای ناهمزمان است، اما تعداد بیشماری دیگر هم در جلو و هم در پشتی وجود دارد.
بازخوانی در جاوا اسکریپت
تا سال ۲۰۱۶، تا زمانی که Promise شیء به زبان معرفی شد. با این حال، توسعه دهندگان جاوا اسکریپت عملکردهای مشابهی را اجرا کرده بودند در سالهای خودشان قبل از رسیدن وعده ها به صحنه.
بیایید به برخی از تفاوتهای بین تماسهای برگشتی و وعدهها نگاهی بیندازیم و ببینیم چگونه میتوانیم از هر تکنیک برای هماهنگ کردن چندین لایه اجرا استفاده کنیم.
یک مثال کلاسیک پاسخ به تماس
توابع ناهمزمان که از تماسهای برگشتی استفاده میکنند، تابعی را به عنوان پارامتر میگیرند، که پس از تکمیل کار فراخوانی میشود. اگر تا به حال از چیزی مانند setTimeout
در مرورگر استفاده کردهاید، از تماسهای برگشتی استفاده کردهاید.
// You can define your callback separately...
let myCallback = () => {
console.log('Called!');
};
setTimeout(myCallback, 3000);
// … but it’s also common to see callbacks defined inline
setTimeout(() => {
console.log('Called!');
}, ۳۰۰۰);
console.log(“This happens first!”);
لیست ۱ به دو روش مختلف می گوید: بعد از ۳۰۰۰ میلی ثانیه، “Callid!” به کنسول. در هر دو مورد، خط “اول اتفاق می افتد!” ابتدا خروجی خواهد گرفت. دلیل آن این است که این تماسهای ناهمزمان هستند و جریان اجرای کد تا زمانی که زمانبندیها در انتظار وقوع هستند ادامه مییابد. این ماهیت برنامه نویسی ناهمزمان است.
تماسهای تودرتو و هرم عذاب
تکلیفهای تماس برای مدیریت کدهای ناهمزمان به خوبی کار میکنند، اما زمانی که شما شروع به هماهنگی چندین عملکرد ناهمزمان میکنید، دشوار میشوند. به عنوان مثال، اگر بخواهیم دو ثانیه صبر کنیم و چیزی را ثبت کنیم، سپس سه ثانیه صبر کنیم و چیز دیگری را ثبت کنیم، سپس چهار ثانیه صبر کنیم و چیز دیگری را ثبت کنیم، نحو ما به سرعت عمیقاً تو در تو می شود. این حالتی است که گاهی اوقات به عنوان جهنم پاسخ به تماس شناخته میشود.
setTimeout(() => {
console.log('First Callback!');
setTimeout(() => {
console.log('Second Callback!');
setTimeout(() => {
console.log('Third Callback!');
}, ۴۰۰۰);
}, ۳۰۰۰);
}, ۲۰۰۰);
فهرست ۲ نگاهی اجمالی به تماس های زشت ارائه می دهد. هرچه کد بیشتری را در تماس های تودرتو قرار دهیم، تشخیص اینکه چه اتفاقی می افتد و هر تماس کجا تکمیل می شود دشوارتر است. این ممکن است یک مثال بی اهمیت به نظر برسد (و همینطور است)، اما ایجاد چندین درخواست وب پشت سر هم بر اساس نتایج بازگشت درخواست قبلی غیر معمول نیست.
مشکل “بازگشت های تودرتو” همیشه ظاهر می شود. در لیست ۳، ما یک مثال درگیرتر را در یک محیط Node.js می بینیم که در آن با چندین فایل سروکار داریم.
fs.readdir('/path/to/dir', function (err, files) {
if (err) {
console.log(err);
return;
}
files.forEach(function (file) {
fs.readFile('/path/to/dir/' + file, 'utf8', function (err, contents) {
if (err) {
console.log(err);
return;
}
// do something with the file
fs.writeFile('/path/to/new/dir/' + file, contents, function (err) {
if (err) {
console.log(err);
return;
}
// do something with the file
});
});
});
});
پیروی از این کد در حال حاضر دشوار است، حتی اگر کار خیلی پیچیده ای انجام نمی دهد و هیچ یک از منطق تجاری واقعی وجود ندارد. فهمیدن اینکه کجا هستید و دستههای فایلی که در چه زمانی باز هستند دشوار است. ما فقط از طریق هر فایل در یک دایرکتوری حلقه می زنیم و محتویات را می خوانیم و از آن در یک فایل جدید در یک دایرکتوری دیگر استفاده می کنیم.
بنابراین، اکنون محدودیت های تماس های برگشتی را مشاهده کرده اید. آنها برای استفاده های ساده مناسب هستند اما در موقعیت های پیچیده تر عالی نیستند. خوشبختانه، جاوا اسکریپت مدرن شی Promise
و کلیدواژه های async
و await
را به عنوان راه حل های انعطاف پذیرتر به ما می دهد.
قول ها
Promises نیاز دارد که تابع به گونهای نوشته شود که یک شی Promise
را برگرداند، که دارای ویژگیهای استاندارد برای مدیریت رفتار بعدی و هماهنگی چند وعده است.
میتوانیم یک تماس مهلت زمانی را در یک Promise
ببندیم تا ببینیم کارها چگونه کار میکنند. میتوانید نمونههایی از مهلت زمانی را در برخی از آگهیهای NPM ببینید و درباره آنها بیشتر بدانید اینجا. فهرست ۴ یک مهلت زمانی مبتنی بر وعده به نام انتظار
ارائه می دهد.
function wait(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
در فهرست ۴، نگاهی اجمالی به نحوه استفاده کد سرویس از Promise
برای مقابله با شرایط ناهمزمان میبینیم. در اصل، تابع wait
ما یک شی Promise
را برمی گرداند، که آرگومان resolve
ارسال شده به سازنده Promise
را فراخوانی می کند. (اتفاقاً، resolve
نمونهای از یک تابع مرتبه بالاتر است.) ممکن است کد در ابتدا پیچیده به نظر برسد، اما ایده ساده است: هنگامی که کد به Promise
منتقل میشود. سازنده تابع resolve
را فراخوانی میکند، به کد کلاینت هشدار داده میشود که Promise
تکمیل شده است. توجه داشته باشید که نام resolve ضروری نیست. هر آرگومانی است که به تابع ارسال شده به Promise
ارسال می شود. پشتیبانی مشابهی برای شرایط خطا وجود دارد.
بعد، در فهرست ۵، میتوانید تابع wait
را مشاهده کنید که با تابع سپس
استفاده میشود.
wait(3000).then(() => {
console.log('Called after 3 seconds');
});
ایده اصلی این است که اکنون میتوانیم wait
را فراخوانی کنیم و آرگومان مربوطه را ارسال کنیم (زمان انتظار
) و مجبور نباشیم در کنترلکننده پاسخ به تماس بهعنوان یک رمز عبور ارسال کنیم. بحث و جدل. در عوض، کنترل کننده در فراخوانی .then()
داده می شود. این امر امکان زنجیرهسازی چندین تماس را با هم باز میکند، همانطور که در فهرست ۶ مشاهده میکنیم.
wait(2000)
.then(() => {
console.log('First Callback!');
return wait(3000);
})
.then(() => {
console.log('Second Callback!');
return wait(4000);
})
.then(() => {
console.log('Third Callback!');
return wait(4000);
});
همچنین میتوانیم با استفاده از تابع ثابت Promise.all()
، همانطور که در فهرست ۷ نشان داده شده است، تماسها را با هم جمع کنیم.
Promise.all([
wait(2000),
wait(3000),
wait(4000)
]).then(() => console.log('Everything is done!'));
Promise همچنین دارای یک روش .race()
است که به محض رفع یا خطا شدن هر یک از وعدهها برمیگردد.
بررسی خطا با وعدهها
اگر Promise
خطایی را برمیگرداند، میتوان آن را با .catch
مدیریت کرد. هنگامی که Promise
ایجاد می شود، آرگومان دوم به همراه resolve
دریافت می کند که معمولاً error
یا err
نام دارد. هنگام فراخوانی، این آرگومان خطا
باعث میشود که کد مشتری متد catch()
را اجرا کند.
جاوااسکریپت ناهمگام/انتظار
به عنوان مثال نهایی، فهرست ۸ نحوه استفاده از تابع wait()
را با کلیدواژههای async
و await
نشان میدهد.
>
async function foo() {
await wait(3000);
console.log('Called after 3 seconds');
}
foo();
فهرست ۸ یک تابع ناهمزمان، foo()
را با کلمه کلیدی async
ایجاد می کند. این کلمه کلیدی به مترجم می گوید که یک کلمه کلیدی wait
در داخل وجود خواهد داشت. این کلمه کلیدی اجازه می دهد تا یک تابع ناهمزمان مانند wait()
را به گونه ای اجرا کند که گویی همزمان است. کد در اینجا مکث میکند تا wait(3000)
کامل شود و سپس ادامه میدهد.
عملکردهای انتظار قابلیت بیشتری دارند، از جمله مدیریت نتایج. برای اطلاعات بیشتر درباره این کلمات کلیدی به نحوه استفاده از async و انتظار در جاوا اسکریپت مراجعه کنید.
پست های مرتبط
پاسخهای تماس جاوا اسکریپت، وعدهها و همگامسازی/انتظار توضیح داده شده است
پاسخهای تماس جاوا اسکریپت، وعدهها و همگامسازی/انتظار توضیح داده شده است
پاسخهای تماس جاوا اسکریپت، وعدهها و همگامسازی/انتظار توضیح داده شده است