جاوا اسکریپت ناهمزمان چگونه کار می کند

در این آموزش، همه چیز را در مورد جاوا اسکریپت Asynchronous یاد خواهید گرفت.
اما قبل از اینکه به آن بپردازیم، باید مطمئن شویم که کد همزمان چیست و چگونه کار می کند.
کد سنکرون چیست؟
وقتی برنامه ای را در جاوا اسکریپت می نویسیم، خط به خط اجرا می شود. هنگامی که یک خط به طور کامل اجرا می شود، تنها و سپس کد به جلو حرکت می کند تا خط بعدی را اجرا کند.
بیایید به یک مثال از این نگاه کنیم:
let greet_one = "Hello" let greet_two = "World!!!" console.log(greet_one) for(let i=0;i<1000000000;i++){ } console.log(greet_two);
حالا اگر مثال بالا را روی دستگاه خود اجرا کنید، متوجه خواهید شد که ابتدا greet_one
ثبت می شود. سپس برنامه چند ثانیه صبر می کند و سپس greet_two
ثبت می کند. این به این دلیل است که کد خط به خط اجرا می شود. به این کد سنکرون می گویند.
این مشکلات زیادی ایجاد می کند. به عنوان مثال، اگر یک قطعه کد خاص 10 ثانیه طول بکشد تا اجرا شود، کد پس از آن تا زمانی که به پایان نرسد نمی تواند اجرا شود و باعث تاخیر می شود.
کد ناهمزمان چیست؟
با کد ناهمزمان، چندین کار می توانند همزمان اجرا شوند، در حالی که وظایف در پس زمینه به پایان می رسد. این همان چیزی است که ما به آن کد غیر مسدود می گوییم. اجرای کدهای دیگر تا زمانی که یک کار ناهمزمان کار خود را تمام کند متوقف نمی شود.
بیایید نمونه ای از کد ناهمزمان را ببینیم:
let greet_one = "Hello" let greet_two = "World!!!" console.log(greet_one) setTimeout(function(){ console.log("Asynchronous"); }, 10000) console.log(greet_two);
در مثال بالا، اگر کد را روی دستگاه خود اجرا کنید، greet_one
و greet_two
را مشاهده خواهید کرد، حتی کدی بین این 2 گزارش وجود دارد.
اکنون setTimeout ناهمزمان است، پس در پسزمینه اجرا میشود و به کدهای بعد از آن اجازه میدهد در حین اجرا اجرا شوند. بعد از 10 ثانیه، Asynchronous
چاپ می شود زیرا ما زمان 10 ثانیه را در setTimeout تعیین کرده ایم تا بعد از 10 ثانیه اجرا شود.
در این آموزش، جاوا اسکریپت ناهمزمان را به طور مفصل مطالعه می کنیم تا بتوانید نحوه نوشتن کد ناهمزمان خود را یاد بگیرید. من فقط می خواستم طعم جاوا اسکریپت غیر همگام را با استفاده از توابع داخلی برای تحریک اشتها به شما نشان دهم.
"یک تابع فراخوانی تابعی است که به عنوان آرگومان به تابع دیگری منتقل می شود، که سپس در داخل تابع خارجی برای تکمیل نوعی روال یا عمل فراخوانی می شود." ( MDN )
بیایید به یک مثال کد نگاه کنیم تا ببینیم چرا استفاده از callback به جای آن مفید است:
function compute(action, x, y){ if(action === "add"){ return x+y }else if(action === "divide"){ return x/y } } console.log(compute("add",10,5)) console.log(compute("divide",10,5))
در مثال بالا دو عملیات داریم. اما اگر بخواهیم عملیات بیشتری اضافه کنیم چه؟ سپس تعداد عبارات if/else افزایش می یابد. این کد طولانی خواهد بود، پس ما به جای آن از callback استفاده می کنیم:
function add(x,y){ return x+y } function divide(x,y){ return x/y } function compute(callBack, x, y){ return callBack(x,y) } console.log(compute(add, 10, 5)) // 2 console.log(compute(divide, 10, 5))
حالا وقتی compute
با سه آرگومان فراخوانی می کنیم، یکی از آنها یک عملیات است. وقتی تابع محاسبات را وارد می کنیم، تابع یک تابع با نام عمل داده شده را برمی گرداند. در پاسخ، آن تابع را فراخوانی می کند و نتیجه را برمی گرداند.
به Callback Hell خوش آمدید
تماس های تلفنی عالی هستند، اما شما نمی خواهید از آنها بیش از حد استفاده کنید. اگر این کار را انجام دهید، چیزی به نام "جهنم پاسخ به تماس" دریافت خواهید کرد. این زمانی اتفاق میافتد که پاسخهای تماس را در چندین سطح عمیق قرار دهید.
شکل جهنم برگشتی مانند یک هرم است و به آن "هرم عذاب" نیز می گویند. حفظ و درک کد را بسیار دشوار می کند. در اینجا یک مثال از جهنم برگشت تماس آورده شده است:
setTimeout(() =>{ console.log("One Second"); setTimeout(() =>{ console.log("Two Seconds"); setTimeout(() =>{ console.log("Three Seconds"); setTimeout(() =>{ console.log("Four Seconds"); setTimeout(() =>{ console.log("Five Seconds"); }, 1000) }, 1000) }, 1000) }, 1000) }, 1000)
پس از گذشت یک ثانیه، کد "یک ثانیه" ثبت می شود. سپس یک تماس دیگر وجود دارد که پس از یک ثانیه دیگر اجرا می شود و "دو ثانیه" ثبت می شود و ادامه می یابد.
ما میتوانیم با استفاده از چیزی به نام Promises
در جاوا اسکریپت ناهمزمان از این جهنم برگشت به تماس فرار کنیم.
یک وعده مکان نگهدار برای نتیجه آینده یک عملیات ناهمزمان است. به عبارت ساده می توان بيان کرد که ظرفی برای ارزش آینده است.
هنگام استفاده از وعدهها، نیازی نیست به تماسهای برگشتی ارتباط برقرار کنیم که به ما کمک میکند از جهنم برگشت به تماس جلوگیری کنیم.
قبل از اینکه به شما نشان دهم چگونه وعدهها از طریق کد کار میکنند، قولها را با استفاده از قیاس بلیت قرعهکشی توضیح میدهم.
وعده ها مانند بلیط قرعه کشی هستند. وقتی بلیت بخت آزمایی میخریم، او میگوید اگر نتیجهمان درست باشد، پول میگیریم. این مثل یک وعده است. قرعه کشی به صورت ناهمزمان انجام می شود و در صورت مطابقت اعداد، پولی که وعده داده شده را دریافت می کنیم.
حالا بیایید به یک مثال کد نگاه کنیم:
const request = fetch('https://course-api.com/react-store-products') console.log(request);
کد بالا از fetch برای درخواست از یک API استفاده می کند. این یک وعده را برمی گرداند که از سرور پاسخ دریافت می کند.

یک قول اینگونه به نظر می رسد. حالت وعده و نتیجه خاصی دارد. هنگامی که یک وعده ایجاد می شود، به صورت ناهمزمان اجرا می شود. وقتی تکلیف داده شده تکمیل شد، آنگاه می گوییم قول تسویه شده است. پس از تسویه قول، می توانیم بر اساس نتیجه قول، حالت وفای یا رد شده داشته باشیم. ما می توانیم این حالت های مختلف را به روش های مختلف در کد خود مدیریت کنیم.
نحوه مصرف وعده ها
ما می توانیم یک وعده را با استفاده از متد then() روی وعده مصرف کنیم. تولید کد کدی است که تکمیل آن ممکن است کمی طول بکشد. کد مصرف کننده کدی است که باید منتظر نتیجه ماند.
پس اگر وعده ای را مصرف کنیم به این معناست که وقتی درخواست می کنیم منتظر نتیجه هستیم. سپس پس از رسیدن به نتیجه، عملیاتی را روی آن نتایج انجام می دهیم.
بیایید به استفاده از مثال بالا ادامه دهیم تا بفهمیم چگونه می توانیم یک وعده را مصرف کنیم.
const request = fetch('https://course-api.com/react-store-products').then((response) =>{ console.log(response); return response.json() }).then((data) =>{ console.log(data); })
ما یک درخواست به API کشور ارائه می کنیم. سپس، پس از درخواست واکشی، از متد then()
برای مصرف وعده استفاده می کنیم. پس از آن، مجموعه ای از اطلاعات مانند هدر، وضعیت و غیره را برمی گردانیم (می توانید آن را در تصویر خروجی زیر مشاهده کنید).
پس ما به طور خاص به داده هایی نیاز داریم که باید آنها را به JSON تبدیل کنیم که یک وعده را برمی گرداند. دادههایی که هنگام درخواست API برگردانده میشوند، در قالب یک وعده بازگردانده میشوند.
برای رسیدگی به این وعده، ما دوباره از متد then() برای ثبت اطلاعات از پاسخ استفاده می کنیم. استفاده از متدهای متعدد then()
روی یک درخواست، وعده های زنجیره ای نامیده می شود.

نحوه برخورد با وعده های رد شده
مصرف وعدهها خوب است، اما یاد گرفتن نحوه برخورد با وعدههای رد شده نیز بسیار مهم است. در موقعیتهای دنیای واقعی، ممکن است زمانهایی پیش بیاید که برنامه ما به دلیل عدم رسیدگی صحیح به وعدههای رد شده از کار بیفتد.
پس بیایید مثالی بزنیم: وعده های خود را در تابعی به نام call()
قرار می دهیم. در HTML یک دکمه ایجاد می کنیم و یک شنونده رویداد به آن اضافه می کنیم. وقتی روی دکمه کلیک می کنیم، تابع call()
را فراخوانی می کند.
در اینجا به نظر می رسد:
index.html
:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Promises</title> </head> <body> <button class="btn">Request</button> <script src="./script.js"></script> </body> </html>
script.js
:
function call(){ const request = fetch('https://course-api.com/react-store-products').then((response) =>{ console.log(response); return response.json() }).then((data) =>{ console.log(data); }) } const btn = document.querySelector("button") btn.addEventListener("click", function(){ call(); })
چرا ما داریم این کار را می کنیم؟ ما در حال تنظیم قول برای رد شدن هستیم. وقتی این کد را اجرا کردیم به قسمت Inspect رفته و تب network را انتخاب کنید. No throttling را روی حالت آفلاین قرار دهید و روی دکمه ارسال درخواست کلیک کنید. شما یک وعده رد شده دریافت خواهید کرد.

پس از کلیک بر روی دکمه، با خطایی مواجه می شویم که ناشی از عدم اتصال به اینترنت است.
اگر اتصال اینترنت کاربر کند باشد، این وضعیت می تواند در دنیای واقعی اتفاق بیفتد. ما در حال درخواست API هستیم که برای آن به اینترنت با سرعت مناسب نیاز داریم. گاهی اوقات ممکن است مشتری با اینترنت خود مشکل داشته باشد. این می تواند به وعده های رد شده منجر شود که خطایی ایجاد می کند که ما هنوز نحوه رسیدگی به آن را ندیده ایم.
حالا یاد می گیریم که این خطا را مدیریت کنیم. ما از then() برای مصرف وعده هایمان استفاده کردیم. مشابه آن، متد catch()
را به آن وعده زنجیر می کنیم. به کد زیر دقت کنید:
function call(){ const request = fetch('https://course-api.com/react-store-products').then((response) =>{ console.log(response); return response.json() }).then((data) =>{ console.log(data); }).catch((err) =>{ alert(err); }) } const btn = document.querySelector("button") btn.addEventListener("click", function(){ call(); })
اکنون متد catch()
یک خطا از وعده رد شده دریافت می کند و پیام را در یک هشدار نمایش می دهد.
ما خطا را دریافت می کنیم زیرا یک وعده رد شده دریافت کردیم که نشان می دهد مشکلی وجود دارد. در بلوک catch() هر کاری که بخواهیم در صورت مواجه شدن با خطا می توانیم انجام دهیم.
همراه با متد catch() یک روش مفید دیگر نیز وجود دارد که به نام finally()
نامیده می شود. ما میتوانیم آن را به وعدههایی زنجیر کنیم که صرف نظر از قبول یا رد شدن وعده، اجرا میشوند.
function call(){ const request = fetch('https://course-api.com/react-store-products').then((response) =>{ console.log(response); return response.json() }).then((data) =>{ console.log(data); }).catch((err) =>{ console.log(err); }).finally(() =>{ console.log("Will always run"); }) } const btn = document.querySelector("button") btn.addEventListener("click", function(){ call(); })
میتوانیم از این متد finally()
برای پاک کردن چیزها پس از فراخوانی API استفاده کنیم. راه های زیادی برای استفاده از متد finally()
وجود دارد.
چگونه یک وعده ایجاد کنیم
ما می دانیم که چگونه وعده ها را مصرف کنیم، اما در مورد ایجاد وعده های خود چطور؟ می توانید این کار را با استفاده از new Promise()
انجام دهید.
ممکن است تعجب کنید - چرا ما به وعده های خود نیاز داریم؟ اولاً، وعده ها ماهیت ناهمزمان دارند. ما میتوانیم با ایجاد وعدههای خود، هر وظیفهای را ایجاد کنیم که ناهمزمان باشد. ما می توانیم آنها را با استفاده از متدهای then()
و catch()
که در بخش بالا یاد گرفتیم مدیریت کنیم.
هنگامی که نحوه ایجاد وعدهها را بدانید، میتوانید هر قطعه کد را ناهمزمان کنید. سپس اگر کد دیگری که در حال اجرا است طول بکشد تا تکمیل شود، اجرای کد را مسدود نخواهد کرد.
بیایید ببینیم که چگونه با استفاده از یک مثال کار می کند:
let lottery = new Promise(function(resolve, reject){ console.log("Lottery is happening"); setTimeout(() => { if(Math.random() >= 0.5){ resolve("You Won!!!") } else{ reject(new Error("Better luck next time")) } }, 5000); })
ابتدا یک وعده با استفاده از new Promise()
ایجاد کردیم. تابعی با دو آرگومان resolve
و reject
خواهد داشت.
زمانی که وظیفه ما موفقیت آمیز بود، resolve
فراخوانی می کنیم و زمانی که کار ناموفق باشد، آن reject
. ما از اصطلاحات قرعه کشی که برای توضیح مفهوم قول در قسمت بالا استفاده کردم استفاده خواهیم کرد.
فرض کنید اگر Math.random()
مقداری زیر یا مساوی 0.5 بدهد، در لاتاری برنده خواهیم شد. در غیر این صورت در قرعه کشی باختیم. اگر شرط درست نباشد، کد یک خطای جدید برای درک بهتر خطا در کنسول پرتاب می کند. پس ما می توانیم خطاهای سفارشی خود را برای درک بهتر به کاربر بیندازیم.
در مثال بالا، اگر Math.rondom()
کمتر از 0.5 باشد، به این معنی است که کاربر قرعه کشی را باخته است. پس ما خطای سفارشی خود را پرتاب می کنیم Better luck next time
تا کاربر بفهمد که در قرعه کشی باخته است.
اکنون سعی خواهیم کرد وعده ای را که ایجاد کرده ایم مصرف کنیم.
let lottery = new Promise(function(resolve, reject){ console.log("Lottery is happening"); setTimeout(() => { if(Math.random() >= 0.5){ resolve("You Won!!!") } else{ reject(new Error("Better luck next time")) } }, 5000); }) lottery.then((response) =>{ console.log(response); }).catch((err) =>{ console.log(err); })
ما وعده را با استفاده از متد then()
مصرف می کنیم. پاسخی را که در resolve()
ارائه کرده ایم چاپ می کند. اگر قول رد شود، خطا را در متد catch()
خواهیم دید. خطا از آرگومان reject()
که در قول خودمان ذکر کردیم، رخ می دهد.
نحوه مصرف Promises با استفاده از Async/wait
مصرف وعده ها با استفاده از متد then() گاهی اوقات ممکن است کثیف شود. پس ما یک روش جایگزین برای مصرف وعده ها به نام async/wait داریم.
فقط به خاطر داشته باشید که async/await از متد then()
در پشت صحنه برای مصرف وعده ها استفاده می کند.
اگر متد then()
داریم چرا از async/await استفاده کنیم؟ ما از async/wait استفاده می کنیم زیرا استفاده از آن آسان است. اگر با استفاده از متد then()
زنجیر کردن متدها به وعدهها را شروع کنیم، زنجیره بسیار طولانی خواهد بود و با اضافه کردن چندین متد then() پیچیده میشود. پس async/wait ساده تر است.
در اینجا نحوه عملکرد async/wait آورده شده است:
const fetchAPI = async function(){ const res = await fetch('https://cat-fact.herokuapp.com/facts') const data = await res.json() console.log(data); } fetchAPI() console.log("FIRST");

در کد بالا، ابتدا fetchAPI() را فراخوانی می کنیم تا رفتار async تابع را ببینیم. سپس "FIRST" را ثبت می کند. پس طبق جاوا اسکریپت ناهمزمان، fetchAPI() باید در پس زمینه اجرا شود و اجرای کد را مسدود نکند. در نتیجه، "FIRST" ثبت می شود و سپس نتیجه fetchAPI نمایش داده می شود.
اکنون، اگر میخواهید وظایف ناهمزمان را در توابع خود انجام دهید، باید آن تابع را با استفاده از کلمه کلیدی async قبل از تابع، ناهمزمان کنید. هر جا که وعدهها برگردانده میشوند، باید از انتظار قبل از آن برای مصرف وعدهها استفاده کنیم.
اکنون ممکن است به این فکر کنید که چگونه باید با خطاها برخورد کنیم؟ برای این کار میتوانیم از try...catch() برای رسیدگی به خطاها در async/await استفاده کنیم.
ما می توانیم try...catch()
در جاوا اسکریپت وانیلی نیز استفاده کنیم. اما همچنین می تواند به ما در رسیدگی به خطاهای جاوا اسکریپت ناهمزمان با async/wait کمک کند.
try...catch()
مشابه متد catch()
در then()
با استفاده از متد catch()
chaining است. در اینجا ما کد موجود در بلوک try
را امتحان خواهیم کرد. اگر با موفقیت اجرا شود، مشکلی وجود ندارد.
اما اگر کد موجود در بلوک try
خطا داشته باشد، می توانیم آن را در بلوک catch
بگیریم. میتوانیم خطاهای موجود در بلوک try را تحلیل کنیم و خطای سفارشی خود را که در بلوک catch
گرفتار میشود، پرتاب کنیم. هنگامی که خطا را در بلوک catch
دریافت می کنیم، می توانیم هر کاری که قصد داریم در صورت مواجه شدن با خطا انجام دهیم.
بیایید ببینیم که چگونه با مثال کدی که استفاده می کنیم کار می کند.
const fetchAPI = async function(){ try{ const res = await fetch('https://cat-fact.herokuapp.com/fact') if(!res.ok){ throw new Error("Custom Error") } const data = await res.json() console.log(data); } catch(err){ console.log(err); } } fetchAPI() console.log("FIRST");
ابتدا کد ناهمزمان را در یک بلوک try
قرار می دهیم. سپس در بلوک catch
خطا را ثبت می کنیم. در بلوک try، اگر res.ok
نادرست باشد، خطای سفارشی خود را با استفاده از throw new Error
که catch
دریافت میکند، پرتاب میکنیم. سپس آن را به کنسول وارد می کنیم.
تا اینجا، ما در مورد کدهای ناهمزمان، متدهای then()
و catch()
و مدیریت کدهای ناهمزمان با async/await یاد گرفتیم. اما اگر بخواهیم مقداری را از یک تابع async با استفاده از async/await برگردانیم چه؟
هنگامی که با کد ناهمزمان کار می کنید، اغلب لازم است مقداری را از یک تابع async
برگردانید تا سایر قسمت های برنامه شما بتوانند از نتیجه عملیات ناهمزمان استفاده کنند.
برای مثال، اگر یک درخواست HTTP برای واکشی دادهها از یک API دارید، میخواهید دادههای پاسخ را به تابع فراخوانی برگردانید تا بتوان آن را پردازش یا به کاربر نمایش داد.
خوب، ما می توانیم این کار را انجام دهیم. به مثال زیر دقت کنید:
const fetchAPI = async function(){ try{ const res = await fetch('https://cat-fact.herokuapp.com/facts') if(!res.ok){ throw new Error("Custom Error") } const data = await res.json() console.log(data); return "Done with fetchAPI" } catch(err){ console.log(err); throw new Error("Custom Error") } } console.log(fetchAPI())
اگر fetchAPI را وارد کنیم، قولی دریافت خواهیم کرد که کامل شده است. شما به خوبی می دانید که چگونه با این وعده ها کنار بیایید. ما آن را با استفاده از متد then()
انجام خواهیم داد.
const fetchAPI = async function(){ try{ const res = await fetch('https://cat-fact.herokuapp.com/facts') if(!res.ok){ throw new Error("Custom Error") } const data = await res.json() console.log(data); return "Done with fetchAPI" } catch(err){ console.log(err); throw new Error("Custom Error") } } fetchAPI().then((msg) =>{ console.log(msg); }).catch((err) =>{ console.log(err); })
اکنون وقتی برنامه خود را اجرا می کنیم، پیام های برگشتی خود را از بلوک try
با استفاده از async/wait وارد شده در کنسول خواهیم دید.
اما اگر خطایی در async/wait وجود داشته باشد چه میشود؟ fetchAPI با متد then() همچنان آن را ثبت میکند و تعریف نشده است.
برای جلوگیری از این اتفاق در بلوک catch دوباره یک خطای جدید پرتاب می کنیم و از متد catch() برای گرفتن آن خطا بعد از متد then() استفاده می کنیم.
سعی کنید متدهای then()
و catch()
را با async/await تغییر دهید. این تمرین خوبی برای درک آنچه در این مقاله آموخته اید خواهد بود.
در جاوا اسکریپت، دو روش متداول برای کار با عملیات ناهمزمان وجود دارد: زنجیره کردن متد then/catch
async/await
. هر دو روش را می توان برای رسیدگی به وعده ها استفاده کرد، که اشیایی هستند که نشان دهنده تکمیل (یا شکست) نهایی یک عملیات ناهمزمان هستند.
زنجیرهبندی روش then/catch
یک روش سنتیتر برای مدیریت عملیات ناهمزمان است، در حالی که async/await
یک نحو جدیدتر است که جایگزینی مختصرتر و خواناتر را ارائه میکند.
نحوه اجرای موازی وعده ها
فرض کنید قصد داریم سه درخواست برای سه پایتخت کشور مختلف ارائه دهیم. ما میتوانیم سه تماس مختلف واکشی انجام دهیم که هر کدام منتظر میمانند تا تماس بالا تکمیل شود.
const fetchAPI = async function(country1,country2,country3){ try{ const res1 = await fetch(`https://restcountries.com/v3.1/name/${country1}`) const res2 = await fetch(`https://restcountries.com/v3.1/name/${country2}`) const res3 = await fetch(`https://restcountries.com/v3.1/name/${country3}`) const data1 = await res1.json() const data2 = await res2.json() const data3 = await res3.json() console.log(data1[0].capital[0]); console.log(data2[0].capital[0]); console.log(data3[0].capital[0]); return "Done with fetchAPI" } catch(err){ console.log(err); throw new Error("Custom Error") } } fetchAPI("canada", "germany", "russia")
در کد بالا، ما سه تماس واکشی انجام می دهیم، سپس آنها را به json() تبدیل می کنیم و سرمایه آنها را ثبت می کنیم.
اما اگر inspect را بزنید و در تب شبکه ببینید، res2 منتظر تکمیل res1 و res3 منتظر تکمیل res2 است.
این می تواند بر عملکرد برنامه ما تأثیر منفی بگذارد. زیرا اگر قولی منتظر وعده دیگری برای تکمیل باشد، می تواند بر عملکرد وب سایت تأثیر منفی بگذارد.

برای غلبه بر این مشکل عملکرد، میتوانیم از چیزی به نام Promise.all
استفاده کنیم. سه درخواست واکشی را به طور همزمان فراخوانی می کند، که در عوض زمان واکشی ما را کاهش می دهد و عملکرد برنامه ما را بهبود می بخشد.
نحوه استفاده از Promise.all()
با کمک Promise.all()، می توانیم چندین وعده را به صورت موازی اجرا کنیم که عملکرد را افزایش می دهد. Project.all() آرایه ای را به عنوان آرگومان می گیرد که وعده هستند و آنها را به صورت موازی اجرا می کند.
let promise1 = new Promise((resolve) =>{ setTimeout(() =>{ resolve("First Promise") }, 2000) }) let promise2 = Promise.resolve("Second Promise") let returnedPromises = Promise.all([promise1,promise2]).then((res) =>{ console.log(res); })
نتیجه استفاده از ()process.all این است که هر دو وعده به صورت موازی اجرا می شدند.

پس از خواندن این آموزش، امیدوارم درک بهتری از جاوا اسکریپت ناهمزمان داشته باشید. برای بحث و پیشنهاد با من در تماس باشید.
شما می توانید من را دنبال کنید:
ارسال نظر