TypeScript: البرمجة غير المتزامنة والأنواع في TypeScript

آخر تحديث: 2026-08-26

تعد البرمجة غير المتزامنة نمطًا أساسيًا في لغة جافا سكريبت — وتضيف لغة تايب سكريبت دعمًا كاملاً للأنواع في العمليات غير المتزامنة من خلال «Promises» العامة وasync/await.

1. أساسيات نوع Promise

(1) الأنواع العامة للوعود

«A Promise» هو نوع عام — تشير Promise<T> إلى أن «قيمة من النوع T ستُنتج في المستقبل»:

TYPESCRIPT
// Synchronization Functions——Direct Return Value
function getUserSync(): string {
  return "Charlie";
}

// Asynchronous Functions——Back Promise<string>
function getUserAsync(): Promise<string> {
  return Promise.resolve("Charlie");
}

// Simulate Web Requests
function fetchUser(id: number): Promise<{ id: number; name: string }> {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ id, name: "User" + id }), 100);
  });
}

(2) استدلال الأنواع في then وcatch

TYPESCRIPT
let promise: Promise<string> = Promise.resolve("hello");

promise
  .then(value => {
    // value Type automatically inferred as string
    console.log(value.toUpperCase());  // ✅
    return value.length;               // Back number → then Chain becomes Promise<number>
  })
  .then(length => {
    // length Type automatically inferred as number
    console.log(length.toFixed(2));    // ✅
  })
  .catch(error => {
    // error Type: any(TypeScript Unable to infer the error type)
    console.log(error.message);
  });

(3) ثبات نوع «Promise» وأمانه

TYPESCRIPT
// Promise It is covariant.——Promise<string> Can be assigned to Promise<string | number>
let p1: Promise<string> = Promise.resolve("hello");
let p2: Promise<string | number> = p1;  // ✅ Covariant Safety

// It doesn't work the other way around.
// let p3: Promise<string> = p2;  // ❌ Promise<string | number> Cannot be assigned Promise<string>

▶ مثال: عميل واجهة برمجة تطبيقات آمنة من حيث النوع

TYPESCRIPT
interface ApiResponse<T> {
  status: number;
  data: T;
  message: string;
}

interface User {
  id: number;
  name: string;
  email: string;
}

interface Product {
  id: number;
  title: string;
  price: number;
}

function apiGet<T>(url: string): Promise<ApiResponse<T>> {
  // Simulation API Call
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        status: 200,
        data: {} as T,  // In actual projects, this is done by JSON.parse Result Assertion
        message: "OK"
      });
    }, 100);
  });
}

// Usage——Each request has a specific return type
async function getUser(id: number): Promise<User> {
  let response = await apiGet<User>(`/api/users/${id}`);
  return response.data;
}

async function getProducts(): Promise<Product[]> {
  let response = await apiGet<Product[]>("/api/products");
  return response.data;
}
▶ جرّب الكود

الناتج:

TEXT 📖 للعرض فقط
// Executed successfully


2. الأنواع في غير متزامن/انتظار

(1) قيم الإرجاع للدوال غير المتزامنة

تُرجع الدوال غير المتزامنة دائمًا وعدًا (Promise) — حتى إذا قمت بإرجاع قيمة عادية، فإن TypeScript تقوم تلقائيًا بتغليفها:

TYPESCRIPT
// Explicit Return Values——Automatic packaging is Promise
async function greet(): Promise<string> {
  return "Hello";  // Equivalent to return Promise.resolve("Hello")
}

// Explicit Return Promise——No double packaging
async function fetchName(): Promise<string> {
  return Promise.resolve("Charlie");  // It won't turn into Promise<Promise<string>>
}

(2) استدلال الأنواع باستخدام await

تنتظر إكمال Promise — نوع await Promise<T> هو T:

TYPESCRIPT
async function example() {
  let name: string = await Promise.resolve("Charlie");      // ✅ Unpack to string
  let count: number = await Promise.resolve(42);          // ✅ Unpack to number
  let user: User = await fetchUser(1);                     // ✅ Unpack to User
}

(3) انتظر «المشكلة» — قد يتم رفض الطلب

TYPESCRIPT
async function riskyOperation(): Promise<number> {
  // This Promise possibly reject
  let result = await mayFail();  // result The type is number——However, an exception may be thrown during runtime.
  return result;
}

async function mayFail(): Promise<number> {
  if (Math.random() > 0.5) {
    throw new Error("Random failure");
  }
  return 42;
}

// Safe coding practice — use try/catch
async function safeOperation(): Promise<number | null> {
  try {
    let result = await mayFail();
    return result;
  } catch {
    return null;
  }
}


3. أنواع المرافق من نوع Promise

(1) «Awaited» — كشف النقاب عن نوع «Promise»

يتضمن TypeScript 4.5 النوع Awaited<T> — وهو عبارة عن تفكيك متكرر لـ Promises:

TYPESCRIPT
type A = Awaited<Promise<string>>;               // string
type B = Awaited<Promise<Promise<number>>>;       // number(Recursive Unpacking)
type C = Awaited<string>;                         // string (non-Promise, returned directly)
type D = Awaited<Promise<string | number>>;       // string | number

(2) الاستخدام العملي — استخراج نوع القيمة المرجعة لدالة غير متزامنة

TYPESCRIPT
async function fetchUser(id: number) {
  let response = await fetch(`/api/users/${id}`);
  return response.json() as Promise<{ id: number; name: string }>;
}

// Extracting the Return Type of an Asynchronous Function(Remove Promise Packaging)
type UserResponse = Awaited<ReturnType<typeof fetchUser>>;
// { id: number; name: string }

(3) أنواع أدوات المساعدة المخصصة لـ Promise

TYPESCRIPT
// Extract Promise Each element in the array Promise Parsed Type
type UnwrapPromiseArray<T> = {
  [K in keyof T]: T[K] extends Promise<infer U> ? U : T[K];
};

type Input = [Promise<string>, Promise<number>, boolean];
type Output = UnwrapPromiseArray<Input>;
// [string, number, boolean]


4. أمان الأنواع في التحكم في التزامن

(1) Promise.all

TYPESCRIPT
async function loadDashboard() {
  // Promise.all Accepting different types of Promise Array
  let [users, products, stats] = await Promise.all([
    fetchUsers(),      // Promise<User[]>
    fetchProducts(),   // Promise<Product[]>
    fetchStats()       // Promise<Stats>
  ]);

  // Each variable has its own type.
  console.log(users.length);        // User[].length
  console.log(products[0].title);   // Product.title
  console.log(stats.totalUsers);    // Stats.totalUsers
}

(2) Promise.allSettled

TYPESCRIPT
type SettledResult<T> = {
  status: "fulfilled";
  value: T;
} | {
  status: "rejected";
  reason: unknown;
};

async function tryMultipleApis() {
  let results = await Promise.allSettled([
    fetchFromApi1(),  // Promise<User>
    fetchFromApi2(),  // Promise<User>
  ]);

  for (let result of results) {
    if (result.status === "fulfilled") {
      console.log(result.value.name);   // ✅ value Type: User
    } else {
      console.log(result.reason);       // unknown
    }
  }
}

(3) Promise.race مع Promise.any

TYPESCRIPT
// race——Take the result that was completed first(Whether success or failure)
async function fetchWithTimeout(url: string, ms: number) {
  let result = await Promise.race([
    fetch(url),
    new Promise<never>((_, reject) =>
      setTimeout(() => reject(new Error("Timeout")), ms)
    )
  ]);
  return result;
}

// any——Take the first successful result(Ignore Failure)
async function fetchWithFallback() {
  let result = await Promise.any([
    fetchFromPrimary(),   // Priority
    fetchFromSecondary(), // Standby
  ]);
  // result Type: All Promise composite types' resolved values
}

▶ مثال: أداة تحميل البيانات المتزامنة

TYPESCRIPT
interface User { id: number; name: string; }
interface Post { id: number; title: string; authorId: number; }
interface Comment { id: number; postId: number; text: string; }

async function fetchAllData(userId: number) {
  let [user, posts, comments] = await Promise.all([
    fetchUser(userId),
    fetchPosts(userId),
    fetchComments(userId)
  ]);

  return { user, posts, comments };
}

async function fetchUser(id: number): Promise<User> {
  return { id, name: "User" + id };
}

async function fetchPosts(userId: number): Promise<Post[]> {
  return [{ id: 1, title: "Article1", authorId: userId }];
}

async function fetchComments(userId: number): Promise<Comment[]> {
  return [{ id: 1, postId: 1, text: "Great Article!" }];
}

async function main() {
  let data = await fetchAllData(1);
  console.log(`User:${data.user.name}`);
  console.log(`Number of Articles:${data.posts.length}`);
  console.log(`Number of comments:${data.comments.length}`);
}

main();
▶ جرّب الكود

الناتج:

TEXT 📖 للعرض فقط
// Executed successfully


5. المكررات غير المتزامنة

(1) AsyncIterable مع for انتظار...of

TYPESCRIPT
async function* asyncCounter(max: number): AsyncGenerator<number> {
  for (let i = 0; i < max; i++) {
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i;
  }
}

async function run() {
  for await (let num of asyncCounter(3)) {
    console.log(num);
  }
  // 0 → 1 → 2(Every interval100ms)
}

(2) أنواع المولدات غير المتزامنة

TYPESCRIPT
interface AsyncGenerator<T> {
  next(): Promise<IteratorResult<T>>;
  return(value?: any): Promise<IteratorResult<T>>;
  throw(e?: any): Promise<IteratorResult<T>>;
  [Symbol.asyncIterator](): AsyncGenerator<T>;
}

▶ مثال: نوع الأداة Awaited في الممارسة العملية

TYPESCRIPT
interface User { id: number; name: string; }
interface Post { id: number; title: string; }

async function fetchUser(id: number): Promise<User> {
  return { id, name: "User" + id };
}

async function fetchPosts(userId: number): Promise<Post[]> {
  return [{ id: 1, title: "Post by " + userId }];
}

type UserType = Awaited<ReturnType<typeof fetchUser>>;   // User
type PostsType = Awaited<ReturnType<typeof fetchPosts>>;  // Post[]

function render(user: UserType, posts: PostsType): string {
  return `${user.name} has ${posts.length} post(s)`;
}

async function main() {
  let user = await fetchUser(1);
  let posts = await fetchPosts(user.id);
  console.log(render(user, posts));
}
▶ جرّب الكود

الناتج:

TEXT 📖 للعرض فقط
User1 has 1 post(s)

❓ أسئلة شائعة

س هل يجب أن تُرجع الدالة async قيمة من نوع Promise؟
ج يُنصح بتحديد ذلك صراحةً. يمكن لـ TypeScript أن يستنتج أن الدالة async تُرجع قيمة من نوع Promise، لكن تحديد ذلك صراحةً async function fn(): Promise<T> يوفر مزيدًا من الوضوح — فهو يساعد في التوثيق ويكشف الأخطاء في وقت التحويل البرمجي. يمكنك حذف نوع القيمة المرجعة للدوال البسيطة، لكن يُنصح بتحديده للدوال المعقدة.
س لماذا يكون نوع الخطأ «غير معروف» في كتلة catch الخاصة بـ Promise؟
ج نظرًا لأن JavaScript تسمح بإلقاء أي قيمة (ولا تقتصر على Error)، فإن TypeScript لا يمكنها ضمان أن تكون القيمة التي يتم التقاطها من نوع Error. يُنصح بإجراء فحص النوع في كتلة catch: if (error instanceof Error) أو استخدام تأكيد النوع: error as Error.
س ماذا يحدث إذا فشلت إحدى الوعود في Promise.all؟
ج تعمل Promise.all وفق مبدأ «كل شيء أو لا شيء» — فإذا تم رفض أي وعد واحد، تُرفض عملية all بأكملها. إذا كنت بحاجة إلى «إكمال جميع الوعود (بغض النظر عن النجاح أو الفشل)»، فاستخدم Promise.allSettled. وإذا كنت بحاجة إلى «نجاح أول وعد»، فاستخدم Promise.any.
س هل يمكن إعادة تحميل الدوال غير المتزامنة؟
ج نعم. تعمل إعادة تحميل الدوال غير المتزامنة بنفس الطريقة تمامًا كما هو الحال مع الدوال المتزامنة — حيث تصف التوقيعات المعاد تحميلها أنواع القيم المرجعة لمجموعات مختلفة من المعلمات (أغلفة Promise)، ويضمن التنفيذ توافق التوقيعات عبر جميع حالات إعادة التحميل.

📖 ملخص

📝 تمارين

  1. المشكلة الأساسية (الصعوبة ⭐): اكتب دالة باسم delay(ms: number): Promise<void> تُرجع وعدًا (Promise) يتم حله بعد ms مللي ثانية. استخدم غير متزامن/انتظار لاستدعاء هذه الدالة وقم بتنفيذ "طباعة رسالة بعد الانتظار لمدة ثانية واحدة".
  2. مشكلة متقدمة (درجة الصعوبة ⭐⭐): اكتب دالة retry<T>(fn: () => Promise<T>, maxRetries: number): Promise<T> — عندما تفشل الدالة fn، فإنها تعيد المحاولة تلقائيًا حتى عدد المرات المحدد بـ maxRetries. قم بتنفيذ ذلك باستخدام try/catch وحلقة.
  3. مشكلة التحدي (الصعوبة ⭐⭐⭐): قم بتنفيذ concurrentLimit<T>(tasks: (() => Promise<T>)[], limit: number): Promise<T[]> — قم بتنفيذ المهام بشكل متزامن، على ألا يتجاوز عدد المهام المنفذة في الوقت نفسه الحد الأقصى المحدد. وبمجرد اكتمال جميع المهام، قم بإرجاع مصفوفة من النتائج (بالترتيب نفسه).
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%