راهنمای مبتدی برای همگام سازی/انتظار جاوا اسکریپت، همراه با مثال
کلمات کلیدی async
و await
در جاوا اسکریپت یک نحو مدرن ارائه میکنند تا به ما در مدیریت عملیات ناهمزمان کمک کنند. در این آموزش، نگاهی عمیق به نحوه استفاده از async/await
برای تسلط بر کنترل جریان در برنامههای جاوا اسکریپت خواهیم داشت. ایجاد تیم های طراحی وب سایت موثر
محتوا:
- نحوه ایجاد یک تابع Async جاوا اسکریپت
- جاوا اسکریپت در انتظار/Async از وعدههای زیر سرپوش استفاده میکند
- بررسی خطا در توابع Async
- اجرای دستورات ناهمزمان به صورت موازی
- ناهمزمان در حلقههای همزمان در انتظار است
- انتظار سطح بالا
- نوشتن کد ناهمزمان با اطمینان
در جاوا اسکریپت، برخی از عملیات ناهمزمان هستند. این بدان معناست که نتیجه یا ارزشی که تولید میکنند فوراً در دسترس نیست.
کد زیر را در نظر بگیرید:
عملکرد fetchDataFromApi() {
کنسول.ورود(داده);
}
fetchDataFromApi();
کنسول.ورود('واکشی داده به پایان رسید');
مفسر جاوا اسکریپت منتظر نمی ماند تا عملکرد ناهمزمان fetchDataFromApi
قبل از رفتن به عبارت بعدی تکمیل شود. در نتیجه، قبل از ثبت اطلاعات واقعی بازگردانده شده از API، Finished Fetching Data
را ثبت می کند.
در بسیاری از موارد، این رفتار مطلوب نیست. خوشبختانه، میتوانیم از کلیدواژههای async
و wait
استفاده کنیم تا برنامهمان را قبل از ادامه کار منتظر کنیم تا عملیات ناهمزمان کامل شود.
این قابلیت در ES2017 به جاوا اسکریپت معرفی شد و در همه مرورگرهای مدرن.
نحوه ایجاد یک تابع Async جاوا اسکریپت
بیایید نگاهی دقیقتر به منطق واکشی دادهها در تابع fetchDataFromApi
بیندازیم. واکشی داده ها در جاوا اسکریپت نمونه بارز عملیات ناهمزمان است.
با استفاده از واکشی API، میتوانیم کاری شبیه به این انجام دهیم:
عملکرد fetchDataFromApi() {
واکشی('https://v2.jokeapi.dev/joke/ برنامه نویسی?type=single')
.سپس(res => res.json())
.سپس(json => کنسول.ورود(json. شوخی));
}
fetchDataFromApi();
کنسول.ورود('واکشی داده به پایان رسید');
در اینجا، ما یک جوک برنامهنویسی را از JokeAPI دریافت میکنیم. پاسخ API در قالب JSON است، بنابراین پس از تکمیل درخواست، آن پاسخ را استخراج می کنیم (با استفاده از روش json()
)، سپس جوک را به کنسول وارد می کنیم.
لطفاً توجه داشته باشید که JokeAPI یک API شخص ثالث است، بنابراین نمیتوانیم کیفیت جوکهایی را که بازگردانده میشوند تضمین کنیم!
اگر این کد را در مرورگر شما یا در Node (نسخه 17.5 و بالاتر با استفاده از پرچم --experimental-fetch
) اجرا کنیم، خواهیم دید که چیزها همچنان در کنسول وارد می شوند. دستور اشتباه
بیایید آن را تغییر دهیم.
کلید کلیدی ناهمگام
اولین کاری که باید انجام دهیم این است که تابع حاوی را به عنوان ناهمزمان برچسب گذاری کنیم. ما می توانیم این کار را با استفاده از کلمه کلیدی async
که در مقابل کلمه کلیدی function
قرار می دهیم انجام دهیم:
ناهمگام عملکرد fetchDataFromApi() {
واکشی('https://v2.jokeapi.dev/joke/ برنامه نویسی?type=single')
.سپس(res => res.json())
.سپس(json => کنسول.ورود(json. شوخی));
}
توابع ناهمزمان همیشه یک وعده را برمیگردانند (در ادامه در مورد آن توضیح خواهیم داد)، بنابراین میتوان با زنجیر کردن یک then()
در فراخوانی تابع، ترتیب اجرای صحیح را دریافت کرد:
fetchDataFromApi()
.سپس(() => {
کنسول.ورود('واکشی داده به پایان رسید');
});
اگر کد را اکنون اجرا کنیم، چیزی شبیه به این میبینیم:
اگر بیل گیتس برای هر بار خرابی ویندوز یک سکه داشت... اوه صبر کنید، او این کار را می کند.
واکشی داده ها به پایان رسید
اما ما نمی خواهیم این کار را انجام دهیم! نحو وعده جاوا اسکریپت می تواند کمی پرمویی شود، و اینجاست که async/await
می درخشد: به ما امکان می دهد کد ناهمزمان را با نحوی بنویسیم که بیشتر شبیه کد همزمان است و خواناتر است.
کلید واژه انتظار
کار بعدی این است که کلمه کلیدی wait
را در مقابل هر عملیات ناهمزمان در تابع خود قرار دهیم. این کار مفسر جاوا اسکریپت را مجبور میکند تا اجرا را «مکث» کند و منتظر نتیجه بماند. ما می توانیم نتایج این عملیات را به متغیرها اختصاص دهیم:
ناهمگام عملکرد fetchDataFromApi() {
const res = انتظار واکشی('https://v2.jokeapi.dev/joke/Programming?type= single');
const json = انتظار پاسخ .json();
کنسول.ورود(json.جوک);
}
ما همچنین باید منتظر نتیجه فراخوانی تابع fetchDataFromApi
باشیم:
انتظار fetchDataFromApi();
کنسول.ورود('واکشی داده به پایان رسید');
متأسفانه، اگر اکنون بخواهیم کد را اجرا کنیم، با خطا مواجه میشویم:
Error Syntax Uncaught: await فقط در توابع ناهمگام، ژنراتورهای ناهمگام و ماژولها معتبر است
این به این دلیل است که ما نمیتوانیم از wait
خارج از تابع async
در یک اسکریپت غیر ماژول استفاده کنیم. ما بعداً با جزئیات بیشتری به این موضوع خواهیم پرداخت، اما در حال حاضر سادهترین راه برای حل مشکل این است که کد فراخوانی را در تابعی از خود بپیچانیم، که ما همچنین به عنوان ناهمگام
علامت گذاری می شود:
ناهمگام عملکرد fetchDataFromApi() {
const res = انتظار واکشی('https://v2.jokeapi.dev/joke/Programming?type= single');
const json = انتظار پاسخ .json();
کنسول.ورود(json.جوک);
}
ناهمگام عملکرد init() {
انتظار fetchDataFromApi();
کنسول.ورود('واکشی داده به پایان رسید');
}
شروع();
اگر اکنون کد را اجرا کنیم، همه چیز باید به ترتیب صحیح خروجی شود:
UDP در دوران کووید بهتر است زیرا از دست دادن های غیرضروری جلوگیری می کند.
واکشی داده ها به پایان رسید
این واقعیت که ما به این دیگ بخار اضافی نیاز داریم مایه تاسف است، اما به نظر من خواندن کد همچنان آسان تر از نسخه مبتنی بر وعده است.
روش های مختلف اعلام توابع ناهمگام
مثال قبلی از دو اعلان تابع نامگذاری شده استفاده می کند (کلید کلیدی function
به دنبال نام تابع)، اما ما به اینها محدود نمی شویم. همچنین میتوانیم عبارات تابع، توابع پیکان و توابع ناشناس را بهعنوان async
علامتگذاری کنیم.
اگر میخواهید درباره تفاوت بین اعلانهای تابع و عبارات توابع تجدید نظر کنید، راهنمای زمان استفاده از کدام.
بیان تابع همگام
یک عبارات تابع زمانی است که یک تابع ایجاد می کنیم و آن را به یک متغیر اختصاص می دهیم. تابع ناشناس است، به این معنی که نامی ندارد. به عنوان مثال:
const fetchDataFromApi = ناهمگام عملکرد() {
const res = انتظار واکشی('https://v2.jokeapi.dev/joke/Programming?type= single');
const json = انتظار پاسخ .json();
کنسول.ورود(json.جوک);
}
این دقیقاً به همان شیوه کد قبلی ما کار می کند.
عملکرد پیکان ناهمگام
توابع پیکان در ES6 به این زبان معرفی شدند. آنها یک جایگزین فشرده برای عبارات تابع هستند و همیشه ناشناس هستند. نحو اصلی آنها به شرح زیر است:
(پارامها) => { تابع بدنه> }
برای علامتگذاری یک تابع پیکان بهعنوان ناهمزمان، کلمه کلیدی async
را قبل از پرانتز باز وارد کنید.
به عنوان مثال، یک جایگزین برای ایجاد یک تابع init
اضافی در کد بالا، قرار دادن کد موجود در یک IIFE، که آن را به عنوان async
علامت گذاری می کنیم:
(ناهمگام () => {
ناهمگام عملکرد fetchDataFromApi() {
const res = انتظار واکشی('https://v2.jokeapi.dev/joke/Programming?type= single');
const json = انتظار پاسخ .json();
کنسول.ورود(json.جوک);
}
انتظار fetchDataFromApi();
کنسول.ورود('واکشی داده به پایان رسید');
})();
تفاوت زیادی بین استفاده از عبارات تابع یا اعلان تابع وجود ندارد: عمدتاً این فقط یک اولویت است. اما چند چیز وجود دارد که باید از آنها آگاه بود، مانند بالا بردن یا این واقعیت که یک تابع فلش خود را به مقدار این
خود متصل نمی کند. برای جزئیات بیشتر می توانید پیوندهای بالا را بررسی کنید.
جاوا اسکریپت در انتظار/Async از وعده های زیر سرپوش استفاده می کند
همانطور که ممکن است قبلاً حدس زده باشید، async/wait
تا حد زیادی، قند نحوی برای وعدهها است. بیایید با جزئیات بیشتری به این موضوع نگاه کنیم، زیرا درک بهتر از آنچه در زیر کاپوت اتفاق میافتد، به درک نحوه عملکرد async/await
کمک زیادی میکند.
اگر مطمئن نیستید وعدهها چیست، یا اگر میخواهید یک تجدید نظر سریع داشته باشید، راهنمای وعدهها.
اولین چیزی که باید از آن آگاه بود این است که یک تابع async
همیشه یک وعده را برمیگرداند، حتی اگر به صراحت به آن نگوییم که این کار را انجام دهد. به عنوان مثال:
ناهمگام عملکرد echo(arg ) {
بازگشت arg;
}
const res = echo(5);
کنسول.ورود(res);
این موارد زیر را ثبت میکند:
وعده { : "اجرا شد"، : 5 }
یک قول میتواند در یکی از سه حالت باشد: در حال انتظار، اجرا شده، یا رد شده. یک وعده زندگی را در حالت معلق شروع می کند. اگر عمل مربوط به عهد با موفقیت انجام شود، قول وفاشده است. اگر عمل ناموفق باشد قولردمی شود. هنگامی که یک وعده محقق شد یا رد شد، اما در حال تعلیق نباشد، همچنین تسویه شده در نظر گرفته می شود.
وقتی از کلمه کلیدی wait
در داخل یک تابع async
برای “مکث” اجرای تابع استفاده میکنیم، آنچه واقعاً اتفاق میافتد این است که منتظر یک قول (یا صریح) هستیم. یا ضمنی) برای قرار گرفتن در وضعیت حل شده یا رد شده.
با تکیه بر مثال بالا، میتوانیم کارهای زیر را انجام دهیم:
ناهمگام عملکرد echo(arg ) {
بازگشت arg;
}
ناهمگام عملکرد getValue() {
const res = انتظار echo(5);
کنسول.ورود(res);
}
getValue();
زیرا تابع echo
یک وعده را برمیگرداند و کلمه کلیدی wait
در داخل تابع getValue
منتظر میماند تا این وعده قبل از ادامه برنامه اجرا شود. ، می توانیم مقدار مورد نظر را در کنسول ثبت کنیم.
Promises یک پیشرفت بزرگ برای کنترل جریان در جاوا اسکریپت است و توسط چندین API مرورگر جدیدتر استفاده می شود – مانند API وضعیت باتری، Clipboard API، واکشی API، API MediaDevices و غیره.
Node همچنین یک عملکرد promisify را به ماژول util
داخلی خود اضافه کرده است. که کدی را تبدیل می کند که از توابع برگشت به تماس برای برگرداندن وعده ها استفاده می کند. و از نسخه 10، توابع موجود در ماژول fs Node میتوانند مستقیماً وعدهها را برگردانند.
تغییر از وعده به async/wait
پس چرا هر یک از اینها برای ما مهم است؟
خب، خبر خوب این است که هر تابعی که یک وعده را برمی گرداند می تواند با async/await
استفاده شود. من نمی گویم که ما باید همه چیزها را async/wait
داشته باشیم (این نحو جنبه های منفی خود را دارد، همانطور که در هنگام رسیدگی به خطا خواهیم دید)، اما باید توجه داشته باشیم که این امکان پذیر است.
ما قبلاً نحوه تغییر تماس واکشی مبتنی بر قول خود را در بالای مقاله برای کار با async/await
دیدهایم، بنابراین اجازه دهید به مثال دیگری نگاهی بیندازیم. در اینجا یک تابع کاربردی کوچک برای دریافت محتویات یک فایل با استفاده از API مبتنی بر وعده Node و روش readFile
آن وجود دارد.
استفاده از Promise.then()
:
const { وعده می دهد : fs } = نیازمند('fs');
const getFileContents = عملکرد(fileName) {
بازگشت fs.readFile (fileName، enc)
}
getFileContents('myFile.md'، 'utf-8')
.سپس((مطالب) => {
کنسول.ورود(contents);
});
With async/await
that becomes:
import { readFile } from 'node:fs/promises';
const getFileContents = function(fileName, enc) {
return readFile(fileName, enc)
}
const contents = await getFileContents('myFile.md', 'utf-8');
console.log(contents);
Note: this is making use of a feature called top-level await, which is only available within ES modules. To run this code, save the file as index.mjs
and use a version of Node >= 14.8.
Although these are simple examples, I find the async/await
syntax easier to follow. This becomes especially true when dealing with multiple then()
statements and with error handling thrown in to the mix. I wouldn’t go as far as converting existing promise-based code to use async/await
, but if that’s something you’re interested in, VS Code can do it for you.