Async/await
في برمجة الحاسوب ، يعتبر نمط async/await ميزة نحوية للعديد من لغات البرمجة تسمح بهيكلة دالة غير متزامنة وغير مانعة بطريقة مشابهة لدالة متزامنة عادية.
يرتبط هذا المفهوم دلاليًا بمفهوم الروتين الفرعي ، وغالبًا ما يُنفذ باستخدام تقنيات مماثلة، ويهدف أساسًا إلى توفير فرص للبرنامج لتنفيذ تعليمات برمجية أخرى أثناء انتظار اكتمال مهمة طويلة الأمد وغير متزامنة، والتي عادةً ما تُمثل بالوعود أو هياكل بيانات مشابهة . توجد هذه الميزة في لغات البرمجة التالية : C# ، [ 1 ] ، C++ ، Python ، F# ، Hack ، Julia ، Dart ، Kotlin ، Rust ، [ 2 ] Nim ، [ 3 ] JavaScript ، و Swift . [ 4 ]
تاريخ
أضافت لغة F# سير العمل غير المتزامن مع نقاط الانتظار في الإصدار 2.0 عام 2007. [ 5 ] وقد أثر ذلك على آلية async/await المضافة إلى لغة C#. [ 6 ]
قام سيمون مارلو، كبير مطوري لغة هاسكل، بإنشاء حزمة async في عام 2010. [ 7 ]
أصدرت مايكروسوفت أول نسخة من لغة C# تدعم async/await في الإصدار التجريبي Async CTP (2011). ثم أُصدرت رسميًا في C# 5 (2012). [ 8 ] [ 1 ] : 10
أضافت لغة بايثون دعمًا لـ async/await مع الإصدار 3.5 في عام 2015 [ 9 ] بإضافة كلمتين مفتاحيتين جديدتين ، asyncو await.
أضافت TypeScript دعمًا لـ async/await مع الإصدار 1.7 في عام 2015. [ 10 ]
أضافت لغة جافا سكريبت دعمًا لـ async/await في عام 2017 كجزء من إصدار ECMAScript 2017 JavaScript.
أضافت لغة Rust دعمًا لـ async/await مع الإصدار 1.39.0 في عام 2019 باستخدام الكلمة asyncالمفتاحية وعامل اللاحقة، وكلاهما تم تقديمه في إصدار 2018 من اللغة. [ 11 ].await
أضافت لغة C++ دعمًا لـ async/await مع الإصدار 20 في عام 2020 مع 3 كلمات مفتاحية جديدة co_return، co_await, co_yield.
أضافت لغة Swift دعمًا لـ async/await مع الإصدار 5.5 في عام 2021، بإضافة كلمتين مفتاحيتين جديدتين async. awaitوقد صدر هذا بالتزامن مع تطبيق عملي لنموذج الممثل باستخدام actorالكلمة المفتاحية [ 12 ] التي تستخدم async/await لتسهيل الوصول إلى كل ممثل من الخارج.
مثال
تستخدم دالة C# الموضحة أدناه، والتي تقوم بتنزيل مورد من عنوان URI وإرجاع طول المورد، نمط async/await هذا:
باستخدام System ؛ باستخدام System.Net.Http ؛ باستخدام System.Threading.Tasks ؛public async Task <int> FindSizeOfPageAsync ( Uri uri ) { HttpClient client = new ( ) ; byte [ ] data = await client.GetByteArrayAsync ( uri ) ; return data.Length ; }- أولًا،
asyncتشير الكلمة المفتاحية إلى لغة C# أن الدالة غير متزامنة، مما يعني أنها قد تستخدم عددًا غير محدود منawaitالتعبيرات وستربط النتيجة بوعد . [ 1 ] : 165-168 - نوع الإرجاع ، ،
System.Threading.Tasks.Task<T>هو نظير C# لمفهوم الوعد، ويُشار هنا إلى أن قيمة النتيجة من النوعint. (لاحظ أنه عند إرجاعvoid،System.Threading.Tasks.Taskيكون الصنف بدون معلمات نوع ). - أول تعبير يُنفذ عند استدعاء هذه الطريقة هو [ 13 ] : 189–190، 344 [ 1 ] : 882، وهي طريقة غير متزامنة أخرى تُرجع قيمة . ولأن هذه الطريقة غير متزامنة، فلن تُنزّل كامل دفعة البيانات قبل الإرجاع. بدلاً من ذلك، ستبدأ عملية التنزيل باستخدام آلية غير حظرية (مثل سلسلة عمليات خلفية )، وتُرجع فورًا قيمة غير محلولة وغير مرفوضة إلى هذه الدالة.
newHttpClient().GetByteArrayAsync(uri)Task<byte[]>Task<byte[]> - مع
awaitإرفاق الكلمة المفتاحية بـTask، ستشرع هذه الوظيفة فورًا في إرجاع إلى المستدعي، الذي يمكنه بعد ذلك متابعة المعالجة الأخرى حسب الحاجة.Task<int> - بمجرد
GetByteArrayAsync()اكتمال التنزيل، سيتم حلّTaskالبيانات التي تم تنزيلها. سيؤدي هذا إلى استدعاء دالة رد نداء ، مما يتسببFindPageSizeAsync()في استمرار التنفيذ عن طريق تعيين تلك القيمة إلىdata. - أخيرًا، تُعيد الدالة قيمة عددية
data.Lengthبسيطة تُشير إلى طول المصفوفة. يُعيد المُصرّف تفسير هذه القيمة على أنها تُحلّل القيمة التي أعادتْها سابقًا، مما يُفعّل دالة رد نداء في الدالة المُستدعِية للقيام بشيء ما باستخدام قيمة الطول هذه.Task
يمكن للدالة التي تستخدم async/await استخدام أي عدد من awaitالتعبيرات، وسيتم التعامل مع كل منها بنفس الطريقة (مع العلم أنه سيتم إرجاع وعد إلى المُستدعي فقط في حالة await الأول، بينما ستستخدم جميع حالات await الأخرى دوال رد نداء داخلية). كما يمكن للدالة الاحتفاظ بكائن وعد مباشرةً وإجراء عمليات أخرى أولاً (بما في ذلك بدء مهام غير متزامنة أخرى)، مما يؤدي إلى تأخير انتظار الوعد حتى الحاجة إلى نتيجته. تحتوي الدوال التي تستخدم الوعود أيضًا على طرق تجميع الوعود التي تسمح للبرنامج بانتظار عدة وعود في وقت واحد أو وفق نمط خاص (مثل C# Task.WhenAll()، [ 1 ] : 174–175 [ 13 ] : 664–665 التي تُرجع قيمة فارغة Taskيتم حلها عند اكتمال جميع المهام في الوسائط). تتميز العديد من أنواع الوعود بميزات إضافية تتجاوز ما يستخدمه نمط async/await عادةً، مثل القدرة على إعداد أكثر من دالة رد نداء للنتيجة أو فحص تقدم مهمة طويلة الأمد.
في حالة لغة C# تحديدًا، وفي العديد من اللغات الأخرى التي تتمتع بهذه الميزة، لا يُعد نمط async/await جزءًا أساسيًا من وقت تشغيل اللغة، بل يُنفذ باستخدام تعابير لامدا أو استمراريات أثناء وقت الترجمة. على سبيل المثال، من المرجح أن يُترجم مُترجم C# الكود أعلاه إلى شيء مشابه لما يلي قبل ترجمته إلى تنسيق بايت كود IL الخاص به :
باستخدام System ؛ باستخدام System.Net.Http ؛ باستخدام System.Threading.Tasks ؛public Task <int> FindSizeOfPageAsync ( Uri uri ) { HttpClient client = new ( ) ; Task < byte [ ] > dataTask = client.GetByteArrayAsync ( uri ) ; Task <int> afterDataTask = dataTask.ContinueWith ( ( originalTask ) = > { return originalTask.Result.Length ; } ) ; return afterDataTask ; }لهذا السبب، إذا احتاجت دالة واجهة إلى إرجاع كائن وعد، ولكنها لا تتطلب awaitفي جسمها انتظار أي مهام غير متزامنة، فإنها لا تحتاج إلى asyncالمُعدِّل ويمكنها بدلاً من ذلك إرجاع كائن وعد مباشرةً. على سبيل المثال، قد تتمكن دالة من توفير وعد يُحل فورًا إلى قيمة نتيجة معينة (مثل Task.FromResult()[ 13 ] : 656 في لغة C# )، أو قد تُرجع ببساطة وعد دالة أخرى يُصادف أنه الوعد المطلوب تمامًا (كما هو الحال عند تأجيل التنفيذ إلى دالة مُحمَّلة بشكل زائد ).
مع ذلك، ثمة محاذير هامة لهذه الخاصية، وهي أنه على الرغم من تشابه الكود مع الكود التقليدي المُعطِّل، إلا أنه في الواقع غير مُعطِّل، وقد يكون متعدد الخيوط، مما يعني إمكانية حدوث العديد من الأحداث المتداخلة أثناء انتظار awaitاكتمال الوعد المُستهدف. على سبيل المثال، قد يواجه الكود التالي، رغم نجاحه الدائم في نموذج مُعطِّل بدون `is` await، أحداثًا متداخلة أثناء `is`، awaitوبالتالي قد يجد أن الحالة المشتركة قد تغيرت دون علمه.
باستخدام System ؛ باستخدام System.Diagnostics ؛ باستخدام System.Net.Http ؛string name = state.name ; HttpClient client = new ( ) ; byte [ ] data = await client.GetByteArrayAsync ( uri ) ;// احتمال حدوث عطل، حيث قد تكون قيمة state.a قد تغيرت // بواسطة معالج حدث قد يكون متداخلاً. Debug.Assert ( name == state.name ) ;إرجاع البيانات . الطول ؛التطبيقات
ج
لا تدعم لغة C الكلمتين await/async. بعض مكتبات الروتينات الفرعية مثل s_task [ 14 ] تحاكي الكلمتين await/async باستخدام وحدات الماكرو.
#include <stdio.h> #include "s_task.h"constexpr int STACK_SIZE = 64 * 1024 / sizeof ( int );// تعريف ذاكرة المكدس للمهام int g_stack_main [ STACK_SIZE ]; int g_stack0 [ STACK_SIZE ]; int g_stack1 [ STACK_SIZE ];void sub_task ( __async__ , void * arg ) { int n = ( int )( size_t ) arg ; for ( int i = 0 ; i < 5 ; ++ i ) { printf ( "المهمة %d، مدة التأخير بالثواني = %d، i = %d \n " , n , n , i ); s_task_msleep ( __await__ , n * 1000 ); // s_task_yield(__await__); } }void main_task ( __async__ , void * arg ) { // إنشاء مهمتين فرعيتين s_task_create ( g_stack0 , sizeof ( g_stack0 ), sub_task , ( void * ) 1 ); s_task_create ( g_stack1 , sizeof ( g_stack1 ), sub_task , ( void * ) 2 );for ( int i = 0 ; i < 4 ; ++ i ) { printf ( "task_main arg = %p, i = %d \n " , arg , i ); s_task_yield ( __await__ ); }// انتظر انتهاء المهام الفرعية للخروج s_task_join ( __await__ , g_stack0 ); s_task_join ( __await__ , g_stack1 ); }int main ( int argc , char * argv []) { s_task_init_system ();// إنشاء المهمة الرئيسية s_task_create ( g_stack_main , sizeof ( g_stack_main ), main_task , ( void * )( size_t ) argc ); s_task_join ( __await__ , g_stack_main ); printf ( "انتهت جميع المهام \n " ); return 0 ; }لغة سي++
في لغة C++، لا توجد asyncكلمة مفتاحية مستقلة، ولكن يوجد ما يُعادل await (المُسمى كذلك co_awaitفي C++ للتأكيد على سياقه في الروتينات الفرعية)، وقد تم تقديمه في C++20 . [ 15 ] تم تقديم فئة المولد في C ++ 23 ، بينما تم تقديم فئة المهمة في C++26 . [ 16 ] داخل الروتين الفرعي، يتم تنفيذ المهمة ، ولكن خارج الروتينات الفرعية، يمكن استدعاؤها باستخدام ، مما يؤدي إلى حظر التنفيذ وإرجاع قيمة . [ 17 ]std::generator<Ref, V, Alloc>std::execution::task<T, Env>co_awaitstd::this_thread::sync_wait()std::optional<std::tuple<Ts...>>
لاحظ أن إرجاع قيمة من روتين فرعي يتم باستخدام الكلمة co_returnالمفتاحية، مما يؤدي إلى إنهاء الروتين الفرعي، بينما co_yieldيقوم بتعليق الروتين الفرعي لإصدار قيمة مرة أخرى إلى المستدعي.
تجدر الإشارة إلى أنَّ std::promise<T>`and` std::future<T>، على الرغم من أنَّهما قد يبدوان كائنين قابلين للانتظار، لا يُنفِّذان أيًّا من الآليات المطلوبة لإرجاعهما من الدوال الفرعية وانتظارهما باستخدام ` co_await. يجب على المبرمجين تنفيذ عدد من الدوال العامة، مثل await_ready()`and` await_suspend()و`and`، await_resume()على نوع الإرجاع حتى يتم انتظار هذا النوع. يمكن الاطلاع على التفاصيل في `cppreference`. [ 18 ]
استيراد std ؛باستخدام std :: optional ؛ باستخدام std :: tuple ؛ باستخدام std :: execution :: task ؛task <int> add ( int a , int b ) noexcept { co_return a + b ; }task <int> test () { int ret = co_await add ( 1 , 2 ) ; std :: println ( "Return {}" , ret ) ; co_return ret ; }int main ( int argc , char * argv []) { optional < tuple < int >> result = std :: this_thread :: sync_wait ( test ()); std :: println ( "النتيجة: {}" , std :: get < 0 > ( result ). value_or ( std :: make_tuple ( -1 )));return 0 ; }سي شارب
في عام 2012، أضافت لغة C# نمط async/await في الإصدار 5.0، والذي تُشير إليه مايكروسوفت باسم نمط البرمجة غير المتزامنة القائم على المهام (TAP). [ 19 ] عادةً ما تُعيد الدوال غير المتزامنة إما void` System.Threading.Tasks.Task_` ( voidمهمة)، أو `_` System.Threading.Tasks.Task<T>( مهمة قيمة)، أو ` _` ( مهمة قيمة) . [ 13 ] : 35 [ 20 ] : 546-547 [ 1 ] : 22، 182 [13] : 651-652 [ 1 ] : 182-184. يُمكن للمستخدم تعريف أنواع مُخصصة تُعيدها الدوال غير المتزامنة من خلال مُنشئي الدوال غير المتزامنة المُخصصة، ولكن هذا سيناريو مُتقدم ونادر. [ 21 ] تُستخدم الدوال غير المتزامنة التي تُعيد `_` في مُعالجات الأحداث ؛ في مُعظم الحالات التي تُعيد فيها الدالة المتزامنة `_` ، يُنصح بإعادة `_` بدلاً من ذلك، لأنها تُتيح مُعالجة استثناءات أكثر سهولة (لاحظ أن `_` غير صالح في C#). [ 22 ]System.Threading.Tasks.ValueTaskvoidSystem.Threading.Tasks.ValueTask<T>voidvoidTaskTask<void>
awaitيجب تعريف الدوال التي تستخدم `is` باستخدام asyncالكلمة المفتاحية `is`. في الدوال التي تُرجع قيمة من النوع `is` ، يجب أن تحتوي Task<T>الدوال المُعرَّفة باستخدام `is` على عبارة إرجاع من النوع `is` قابلة للتخصيص لـ ` is` بدلاً من `is` ؛ حيث يقوم المُصرِّف بتغليف القيمة في النوع العام. من الممكن أيضًا تعريف الدوال التي تُرجع قيمة من النوع `is` أو التي تم تعريفها بدون `is` .asyncTTask<T>Task<T>awaitTaskTask<T>async
تقوم الطريقة غير المتزامنة التالية بتنزيل البيانات من عنوان URL باستخدام await. ولأن هذه الطريقة تُصدر مهمة لكل عنوان URI قبل اشتراط الإكمال باستخدام awaitالكلمة المفتاحية ، يمكن تحميل الموارد في نفس الوقت بدلاً من انتظار انتهاء تحميل المورد الأخير قبل البدء في تحميل المورد التالي.
namespace Wikipedia.Examples ;باستخدام System ؛ باستخدام System.Collections.Generic ؛ باستخدام System.Net.Http ؛ باستخدام System.Threading.Tasks ؛public class Example { public async Task <int> SumPageSizesAsync ( IEnumerable <Uri> uris ) { HttpClient client = new ( ) ; int total = 0 ; List < Task < byte [] >> loadUriTasks = new ( ) ;foreach ( Uri uri in uris ) { byte [] loadUriTask = client . GetByteArrayAsync ( uri ); loadUriTask . Add ( loadUriTask ); }foreach ( Task < byte [] >> loadUriTask in loadUriTasks ) { statusText . Text = $"تم العثور على {total} بايت ..." ; byte [] resourceAsBytes = await loadUriTask ; total += resourceAsBytes . Length ; }statusText.Text = $ "تم العثور على {total} بايت إجمالاً" ;أعد المجموع الكلي ؛ }static async Task Main ( string [] args ) { List < Uri > uris = new { new Uri ( "https://en.wikipedia.org" ), new Uri ( "https://www.microsoft.com" ), new Uri ( "https://www.github.com" ) }int totalBytes = await SumPageSizesAsync ( uris ); Console . WriteLine ( $"Total bytes downloaded: {totalbytes}" ); } }فا#
في عام ٢٠٠٧، أضافت لغة F# ميزة سير العمل غير المتزامن مع الإصدار ٢.٠. [ ٢٣ ] تُنفَّذ هذه الميزة باستخدام تعبيرات حسابية (CE ). ويمكن تعريفها دون تحديد أي سياق خاص (كما هو الحال asyncفي لغة C#). تُلحق ميزة سير العمل غير المتزامن في F# علامة تعجب (!) بالكلمات المفتاحية لبدء المهام غير المتزامنة.
تقوم الدالة غير المتزامنة التالية بتنزيل البيانات من عنوان URL باستخدام سير عمل غير متزامن:
let asyncSumPageSizes ( uris : #seq <Uri> ) : Async <int> = async { use httpClient = new HttpClient ( ) let ! pages = uris | > Seq.map ( httpClient.GetStringAsync >> Async.AwaitTask ) | > Async.Parallel return pages | > Seq.fold ( fun accumulator current - > current.Length + accumulator ) 0 }جافا
لا تحتوي لغة جافا على الكلمات المفتاحية async`and` await، ولكن يمكن محاكاتها باستخدام java.util.concurrentحزمة `and`، مثل فئة java.util.concurrent.CompletableFuture<T>`and` (التي تم تقديمها في جافا 8). وقد تم تحسين البرمجة غير المتزامنة لاحقًا في جافا 21 مع تقديم الخيوط الافتراضية ونطاقات المهام المنظمة.
package org.wikipedia.examples ;استيراد java.util.concurrent.CompletableFuture ؛ استيراد java.util.concurrent.ExecutionException ؛ استيراد java.util.concurrent.ExecutorService ؛ استيراد java.util.concurrent.Executors ؛ استيراد java.util.concurrent.Future ؛ استيراد java.util.concurrent.StructuredTaskScope ؛public class AsyncExample { public String fetchData () { // محاكاة عملية تستغرق وقتًا طويلاً (مثل طلب شبكة، استعلام قاعدة بيانات) try { Thread . sleep ( 2000 ); } catch ( InterruptedException e ) { Thread . currentThread (). interrupt (); } return "بيانات من مصدر بعيد" ; }public CompletableFuture <String> fetchDataAsync ( ) { return CompletableFuture.supplyAsync ( () -> fetchData ( )) ; }public static void main ( String [] args ) throws ExecutionException , InterruptedException { AsyncExample example = new AsyncExample ();// باستخدام CompletableFuture ( Java 8) System.out.println ( "بدء العملية غير المتزامنة..." ) ;CompletableFuture <String> future = example.fetchDataAsync ( ) ;System.out.println ( " جاري القيام بأعمال أخرى... " ) ;// انتظر النتيجة (على غرار 'await') String result = future.get ( ) ; System.out.printf ( " تم استلام النتيجة: %s%n" , result ) ;// استخدام الخيوط الافتراضية (جافا 21): ExecutorService extor = Executors.newVirtualThreadPerTaskExecutor (); System.out.println ( " بدء عملية الخيط الافتراضي..." ) ;Future <String> fut = extor . Submit (() - > { return example .fetchData (); });System.out.println ( " جاري القيام بأعمال أخرى... " ) ;String result = future.get ( ); System.out.printf ( " Received result: %s % n " , result ) ; extor.shutdown ( ) ;// باستخدام StructuredTaskScope ( Java 21 ) حاول ( StructuredTaskScope.ShutdownOnFailure scope = new StructuredTaskScope.ShutdownOnFailure ( ) ) { StructuredTaskScope.Subtask <String> dataTask = scope.fork ( example :: fetchData ) ; System.out.println ( " جاري تنفيذ مهام أخرى ... " ) ;scope.join (); // انتظر حتى تنتهي جميع المهام scope.throwIfFailed ( ) ; // انشر الخطأ في حال حدوثهString result = future.get (); System.out.printf ( " Received result: %s % n " , result ) ; } } }جافا سكريبت
لا يُمكن استخدام عامل await في جافا سكريبت (وتايب سكريبت) إلا من داخل دالة غير متزامنة أو في المستوى الأعلى من وحدة نمطية . إذا كان المُعامل عبارة عن وعد ، فسيستأنف تنفيذ الدالة غير المتزامنة عند تحقق الوعد (إلا إذا رُفض الوعد، وفي هذه الحالة سيُثار خطأ يُمكن معالجته باستخدام معالجة الاستثناءات العادية في جافا سكريبت ). أما إذا لم يكن المُعامل وعدًا، فسيتم إرجاع المُعامل نفسه فورًا. [ 24 ]
توفر العديد من المكتبات كائنات وعد يمكن استخدامها مع await، طالما أنها تتوافق مع مواصفات وعود جافا سكريبت الأصلية. مع ذلك، لم تكن وعود مكتبة jQuery متوافقة مع Promises/A+ حتى إصدار jQuery 3.0. [ 25 ]
فيما يلي مثال (معدل من هذه المقالة [ 26 ] ):
واجهة DBResponse { id : سلسلة نصية ; rev? : سلسلة نصية ; ok? : قيمة منطقية ; }واجهة المستند { _id : سلسلة نصية ; _rev? : سلسلة نصية ; [ key : سلسلة نصية ] : أي ; }واجهة قاعدة البيانات { post ( doc : object ) : Promise < DBResponse > ; get ( id : string ) : Promise < Document > ; }declare const db : Database ;async function createNewDoc ( ) : Promise <Document> { const response : DBResponse = await db.post ( { } ) ; const doc : Document = await db.get ( response.id ) ; return doc ; }async function main () : Promise <void> { try { const doc : Document = await createNewDoc ( ); console.log ( doc ) ; } catch ( err : Error ) { console.error ( " خطأ في إنشاء أو جلب المستند: " , err ) ; } }رئيسي ()؛يتضمن الإصدار الثامن من Node.js أداةً تُمكّن من استخدام طرق الاستدعاء القائمة على ردود الاتصال في المكتبة القياسية كوعود. [ 27 ]
كوتلين
تستخدم لغة Kotlin الدوال الفرعية مع طرق async()(from kotlinx.coroutines.CoroutineScope) و await()(from kotlinx.coroutines.Deferred<T>)، بدلاً من الكلمات المفتاحية true async/ await. [ 28 ] [ 29 ]
asyncعلى سبيل المثال، الاستخدامات والأساليب التالية await()للتنفيذ المتوازي.
استيراد kotlinx.coroutines.* استيراد kotlin.system.measureTimeMillisدالة معلقة لجلب البيانات الأولى: سلسلة نصية { تأخير ( 1000 ) // تحاكي مكالمة شبكة إرجاع "بيانات من الخادم أ" }دالة معلقة لجلب البيانات الثانية: سلسلة نصية { تأخير ( 1000 ) // تحاكي مكالمة شبكة إرجاع "بيانات من الخادم ب" }suspend fun main () = coroutineScope { val time = measureTimeMillis { // بدء كلا طلبي الشبكة في نفس الوقت في الخلفية val deferredOne : Deferred < String > = async { fetchFirstData () } val deferredTwo : Deferred < String > = async { fetchSecondData () }// انتظر حتى تنتهي كلتا النتيجتين val resultOne = deferredOne . await () val resultTwo = deferredTwo . await ()println ( "النتائج المجمعة: $ resultOne & $ resultTwo " ) } println ( "اكتمل في $ time مللي ثانية" ) }بيرل
كانت وحدة Future::AsyncAwait [ 30 ] موضوع منحة من مؤسسة بيرل في سبتمبر 2018. [ 31 ]
بايثون
أضافت لغة بايثون 3.5 (2015) [ 32 ] دعمًا لـ async/await كما هو موضح في PEP 492 (الذي كتبه ونفذه يوري سيلفانوف ). [ 33 ]
استيراد asyncioasync def main () -> None : print ( " hello" ) await asyncio.sleep ( 1 ) print ( " world" )إذا كان __name__ يساوي " __main__" : asyncio.run ( main ( ))الصدأ
في 7 نوفمبر 2019، تم إطلاق async/await في النسخة المستقرة من لغة Rust. [ 34 ] تُحوّل الدوال غير المتزامنة في Rust الدوال العادية التي تُعيد قيمًا تُطبّق std::future::Futureالسمة. ويتم تنفيذها حاليًا باستخدام آلة الحالة المحدودة . [ 35 ]
asyncهو مُحدِّد يُشير إلى أن التعبير يُعيد قيمة std::future::Future(بدلاً من حظر الخيط) [ 36 ] ، بينما awaitهو عامل (يُستخدم مع )، والذي يُعلِّق التنفيذ حتى تصبح النتيجة جاهزة. [ 37 ].awaitstd::future::Future
// في ملف Cargo.toml الخاص بالحزمة، نحتاج إلى `futures = "0.3.0"` في قسم التبعيات، // حتى نتمكن من استخدام حزمة futuresextern crate futures ; // لا يوجد منفذ حاليًا في مكتبة `std`.استخدم std :: future :: Future ؛// هذا يُحوّل إلى شيء مثل // `fn async_add_one(num: u32) -> impl Future<Output = u32>` async fn async_add_one ( num : u32 ) -> u32 { num + 1 }async fn example_task () -> impl Future < Output = () > { let number = async_add_one ( 5 ) .await ; println! ( "5 + 1 = {}" , number ); }fn main () { // إنشاء الكائن Future لا يبدأ التنفيذ. let future = example_task ();// لا يتم تنفيذ `Future` إلا عند استطلاع حالته فعليًا، على عكس جافا سكريبت. futures :: executor :: block_on ( future ); }سويفت
أضافت Swift 5.5 (2021) [ 38 ] دعمًا لـ async/await كما هو موضح في SE-0296. [ 39 ]
func getNumber ( ) async throws - > Int { try await Task.sleep ( nanoseconds : 1_000_000_000 ) return 42 }المهمة { let first = try await getNumber () let second = try await getNumber () print ( first + second ) }الإيجابيات والانتقادات
يُعد نمط async/await جذابًا بشكل خاص لمصممي اللغات التي لا تمتلك أو تتحكم في وقت تشغيلها الخاص ، حيث يمكن تنفيذ async/await فقط كتحويل إلى آلة حالة في المترجم. [ 40 ]
يزعم المؤيدون أنه يمكن كتابة شيفرة غير متزامنة وغير حظرية باستخدام async/await بحيث تبدو شبيهة إلى حد كبير بالشيفرة المتزامنة والحظرية التقليدية. وقد جادل البعض بأن await هي أفضل طريقة لكتابة الشيفرة غير المتزامنة في برامج تمرير الرسائل ؛ حيث ذُكرت مزاياها، لا سيما قربها من الشيفرة الحظرية، وسهولة قراءتها، وتقليلها للشيفرة النمطية . [ 41 ]
يُشير منتقدو نمط async/await إلى أنه يُؤدي غالبًا إلى جعل الشيفرة المحيطة به غير متزامنة أيضًا؛ وأن طبيعته المُعدية تُقسّم بيئات مكتبات اللغات بين المكتبات وواجهات برمجة التطبيقات المتزامنة وغير المتزامنة، وهي مشكلة تُعرف غالبًا باسم "تلوين الدوال". [ 42 ] تُسمى البدائل لنمط async/await التي لا تُعاني من هذه المشكلة "بدائل عديمة اللون". ومن أمثلة التصاميم عديمة اللون : goroutines في لغة Go و virtual threads في لغة Java . [ 43 ]
انظر أيضاً
مراجع
- 1 2 3 4 5 6 7 سكيت، جون (23 مارس 2019). لغة سي شارب بتفصيل . مانينغ. ISBN 978-1617294532.
- ↑ "الإعلان عن إصدار Rust 1.39.0" . تم الاطلاع عليه بتاريخ 7 نوفمبر 2019 .
- ↑ "std/async" . مؤرشف من الأصل بتاريخ 29 سبتمبر 2025. تم الاطلاع عليه بتاريخ 19 يناير 2020 .
- ↑ "التزامن - لغة برمجة سويفت (سويفت 5.5)" . docs.swift.org . تم الاطلاع عليه بتاريخ 28-09-2021 .
- ↑ سايم، دون؛ بيتريتشيك، توماس؛ لوموف، ديمتري (2011). "نموذج البرمجة غير المتزامنة في لغة F#" . الجوانب العملية للغات التصريحية . سلسلة محاضرات في علوم الحاسوب. المجلد 6539. سبرينغر لينك. الصفحات 175-189 . doi : 10.1007/978-3-642-18378-2_15 . ISBN 978-3-642-18377-5تم الاطلاع عليه بتاريخ 29-04-2021 .
- ↑ "التاريخ المبكر لـ F#، HOPL IV" . مكتبة ACM الرقمية . تم الاسترجاع في 29-04-2021 .
- ↑ "async: تشغيل عمليات الإدخال والإخراج بشكل غير متزامن وانتظار نتائجها" . Hackage .
- ↑ هيلسبرغ، أندرس. "أندرس هيلسبرغ: تقديم البرمجة غير المتزامنة - تبسيط البرمجة غير المتزامنة" . قناة 9 MSDN . مايكروسوفت . تم الاطلاع عليه في 5 يناير 2021 .
- ↑ "ما الجديد في بايثون 3.5 — وثائق بايثون 3.9.1" . docs.python.org . تم الاطلاع عليه بتاريخ 5 يناير 2021 .
- ↑ غاوراف، سيث (30 نوفمبر 2015). "الإعلان عن TypeScript 1.7" . TypeScript . مايكروسوفت . تم الاطلاع عليه في 5 يناير 2021 .
- ↑ ماتساكيس، نيكو. "Async-await على لغة Rust المستقرة! | مدونة Rust" . blog.rust-lang.org . مدونة Rust . تم الاطلاع عليه في 5 يناير 2021 .
- ↑ "التزامن - لغة برمجة سويفت (سويفت 5.6)" .
- 1 2 3 4 5 البحاري، يوسف (2022). C # 10 باختصار . أورايلي. رقم ISBN 978-1-098-12195-2.
- ↑ "s_task - مكتبة روتينية قابلة للانتظار للغة C" . GitHub .
- ↑ "لجنة ISO C++ تعلن أن تصميم C++20 أصبح الآن مكتمل الميزات" . 25 فبراير 2019.
- ↑ ديتمار كول، مايكل نادولسكي (18 مايو 2025). "إضافة نوع مهمة روتينية فرعية" (ملف PDF) . open-std.org . WG21.
- ↑ cppreference.com. "std::this_thread::sync_wait" . cppreference.com . cppreference.com . تم الاطلاع عليه بتاريخ 15 فبراير 2026 .
- ↑ "الروتينات الفرعية (C++20)" .
- ↑ "نمط غير متزامن قائم على المهام" . مايكروسوفت . تم الاطلاع عليه بتاريخ 28 سبتمبر 2020 .
- ↑ برايس، مارك ج. (2022). سي شارب 8.0 و.نت كور 3.0 - تطوير تطبيقات حديثة متعددة المنصات: بناء تطبيقات باستخدام سي شارب، و.نت كور، وإنتيتي فريم وورك كور، وإيه إس بي.نت كور، وإم إل.نت باستخدام فيجوال ستوديو كود . باكْت. رقم ISBN 978-1-098-12195-2.
- ↑ تيبلياكوف، سيرجي (11 يناير 2018). "توسيع نطاق الأساليب غير المتزامنة في لغة سي شارب" . دعم المطورين . تم الاسترجاع في 30 أكتوبر 2022 .
- ↑ ستيفن كليري، Async/Await - أفضل الممارسات في البرمجة غير المتزامنة
- ↑ "تقديم سير العمل غير المتزامن في F#" . 10 أكتوبر 2007.
- ↑ "await - JavaScript (MDN)" . تم الاطلاع عليه بتاريخ 2 مايو 2017 .
- ↑ "دليل ترقية jQuery Core 3.0" . تم الاطلاع عليه بتاريخ 2 مايو 2017 .
- ↑ "ترويض الوحش غير المتزامن باستخدام ES7" . تم الاطلاع عليه بتاريخ 12 نوفمبر 2015 .
- ↑ مؤسسة Node.js (30 مايو 2017). "Node v8.0.0 ( الحالي) - Node.js" . Node.js.
- ↑ JetBrains sro (2 أبريل 2026). "تقنيات البرمجة غير المتزامنة" . kotlinlang.org . JetBrains sro
- ↑ JetBrains sro (27 فبراير 2025). "تأليف الدوال المعلقة" . kotlinlang.org . JetBrains sro
- ↑ "Future::AsyncAwait - صيغة الروتين الفرعي المؤجل للمستقبلات" .
- ↑ "تصويتات منح سبتمبر 2018 - مؤسسة بيرل" . news.perlfoundation.org . تم الاطلاع عليه بتاريخ 26 مارس 2019 .
- ↑ "إصدار بايثون 3.5.0" .
- ↑ "PEP 492 – Coroutines with async and await syntax" .
- ↑ ماتساكيس، نيكو. "Async-await على لغة Rust المستقرة!" . مدونة Rust . تم الاطلاع عليه بتاريخ 7 نوفمبر 2019 .
- ↑ أوبيرمان، فيليب. "Async/Await" . تم الاطلاع عليه بتاريخ 28 أكتوبر 2020 .
- ↑ مطورو لغة Rust (14 يوليو 2026). "الكلمة المفتاحية async" . doc.rust-lang.org . مطورو لغة Rust.
- ↑ مطورو لغة Rust (14 يوليو 2026). "الكلمة المفتاحية await" . doc.rust-lang.org . مطورو لغة Rust.
- ↑ "نسخة مؤرشفة" . مؤرشفة من الأصل بتاريخ 23 يناير 2022. تم الاطلاع عليها بتاريخ 20 ديسمبر 2021 .
{{cite web}}: CS1 maint: archived copy as title ( link ) - ↑ "SE-0296" . GitHub .
- ↑ "الجزء الثالث من البرمجة غير المتزامنة - كيف يقوم مترجم لغة C# بتنفيذ الدوال غير المتزامنة" .
- ↑ هير "لا أخطاء". ثماني طرق للتعامل مع عمليات الإرجاع غير المحظورة في برامج تمرير الرسائل، مؤتمر CPPCON، 2018
- ↑ "ما لون وظيفتك؟ "
- ↑ "الخيوط الافتراضية" .
- تدفق التحكم
