לדלג לתוכן

4.5 פרומיסים ו async await תרגול

תרגול - פרומיסים ו-async/await


תרגיל 1 - פרומיסים בסיסיים

חלק א

כתבו פונקציה delay(ms) שמחזירה Promise שמתקיים אחרי ms מילישניות:

async function demo() {
    console.log("Start");
    await delay(2000);
    console.log("2 seconds later");
}

חלק ב

כתבו פונקציה randomSuccess(chance) שמחזירה Promise שמצליח בהסתברות של chance (מספר בין 0 ל-1) ונכשל אחרת. ה-Promise מתקיים אחרי שניה:

randomSuccess(0.7)
    .then(() => console.log("Success!"))
    .catch(() => console.log("Failed!"));
// 70% chance of "Success!", 30% chance of "Failed!"

תרגיל 2 - שרשור Promises

כתבו סימולציה של API שמחזיר נתונים עם השהייה. ממשו שלוש פונקציות:

  • fetchUser(id) - מחזירה Promise עם אובייקט { id, name, departmentId } אחרי 500ms
  • fetchDepartment(id) - מחזירה Promise עם { id, name, managerId } אחרי 500ms
  • fetchManager(id) - מחזירה Promise עם { id, name, email } אחרי 500ms

כתבו פונקציה getUserManagerEmail(userId) ששולפת את האימייל של המנהל של המחלקה של המשתמש:

// using promise chaining:
getUserManagerEmail(1).then(email => console.log(email));

// using async/await:
const email = await getUserManagerEmail(1);

כתבו את הפונקציה בשתי גרסאות: אחת עם שרשור then ואחת עם async/await.


תרגיל 3 - Promise.all לעומת ריצה סדרתית

נתונות שלוש פונקציות שכל אחת לוקחת שניה:

function fetchUsers() {
    return new Promise(resolve =>
        setTimeout(() => resolve(["Alice", "Bob"]), 1000)
    );
}

function fetchPosts() {
    return new Promise(resolve =>
        setTimeout(() => resolve(["Post 1", "Post 2"]), 1000)
    );
}

function fetchComments() {
    return new Promise(resolve =>
        setTimeout(() => resolve(["Comment 1"]), 1000)
    );
}

חלק א

כתבו פונקציה loadAllSequential() שטוענת את שלושתם סדרתית (אחד אחרי השני). מדדו כמה זמן לוקח.

חלק ב

כתבו פונקציה loadAllParallel() שטוענת את שלושתם במקביל. מדדו כמה זמן לוקח.

השתמשו ב-console.time ו-console.timeEnd למדידה:

console.time("sequential");
const data = await loadAllSequential();
console.timeEnd("sequential"); // ~3 seconds

console.time("parallel");
const data2 = await loadAllParallel();
console.timeEnd("parallel"); // ~1 second

תרגיל 4 - טיפול בשגיאות

כתבו פונקציה safeFetch(url) שעוטפת fetch עם הטיפולים הבאים:

  • אם ה-response לא ok (סטטוס לא 200-299), זורקת שגיאה עם הסטטוס
  • אם יש שגיאת רשת, תופסת אותה ומחזירה אובייקט שגיאה
  • מדפיסה "Done" בכל מקרה (הצלחה או כישלון)
async function safeFetch(url) {
    // your code here
}

// returns { ok: true, data: {...} } on success
// returns { ok: false, error: "..." } on failure

בנוסף, כתבו פונקציה fetchMultiple(urls) שמקבלת מערך URLs ומחזירה את התוצאות של כולם (גם אם חלקם נכשלים). השתמשו ב-Promise.allSettled.


תרגיל 5 - retry עם backoff

כתבו פונקציה retryAsync(fn, maxRetries, initialDelay) שמנסה להריץ פונקציה אסינכרונית עד שהיא מצליחה:

  • fn - פונקציה אסינכרונית להרצה
  • maxRetries - מספר ניסיונות מקסימלי (ברירת מחדל 3)
  • initialDelay - השהייה לפני ניסיון חוזר ראשון במילישניות (ברירת מחדל 1000)
  • ההשהייה מוכפלת בכל ניסיון (exponential backoff): 1000ms, 2000ms, 4000ms...
  • מדפיסה את מספר הניסיון בכל פעם
  • אם כל הניסיונות נכשלו, זורקת את השגיאה האחרונה
let callCount = 0;

async function unreliableApi() {
    callCount++;
    if (callCount < 3) {
        throw new Error("Server error");
    }
    return { data: "success" };
}

const result = await retryAsync(unreliableApi);
// Attempt 1 failed: Server error (retrying in 1000ms)
// Attempt 2 failed: Server error (retrying in 2000ms)
// Attempt 3 succeeded!
console.log(result); // { data: "success" }

תרגיל 6 - Promise.race - timeout

חלק א

כתבו פונקציה withTimeout(promise, ms) שמקבלת Promise וזמן מקסימלי. אם ה-Promise לא מסתיים בזמן, מחזירה שגיאת timeout:

const fast = delay(500).then(() => "fast result");
const slow = delay(5000).then(() => "slow result");

console.log(await withTimeout(fast, 1000)); // "fast result"
console.log(await withTimeout(slow, 1000)); // throws Error: "Timeout after 1000ms"

חלק ב

כתבו פונקציה fetchFastest(urls) שמקבלת מערך URLs ומחזירה את התוצאה של הבקשה הראשונה שמצליחה (השתמשו ב-Promise.any):

const result = await fetchFastest([
    "https://slow-api.com/data",
    "https://fast-api.com/data",
    "https://medium-api.com/data"
]);
// returns response from whichever is fastest

תרגיל 7 - עיבוד תור משימות

כתבו מחלקה AsyncQueue שמעבדת משימות אסינכרוניות בתור:

  • enqueue(asyncFn) - מוסיפה משימה לתור ומחזירה Promise שיתקיים כשהמשימה תסתיים
  • getQueueSize() - מחזיר כמה משימות ממתינות
  • isProcessing() - מחזיר אם יש משימה שרצה כרגע

המשימות רצות אחת אחרי השנייה (לא במקביל). כל משימה מתחילה רק אחרי שהקודמת סיימה.

const queue = new AsyncQueue();

// these should run sequentially, not in parallel
queue.enqueue(async () => {
    await delay(1000);
    console.log("Task 1 done");
    return "result 1";
});

queue.enqueue(async () => {
    await delay(500);
    console.log("Task 2 done");
    return "result 2";
});

const result3 = await queue.enqueue(async () => {
    await delay(200);
    console.log("Task 3 done");
    return "result 3";
});

// Output (in order):
// "Task 1 done" (after 1 second)
// "Task 2 done" (after 1.5 seconds)
// "Task 3 done" (after 1.7 seconds)

console.log(result3); // "result 3"

בונוס: הוסיפו פרמטר concurrency ל-constructor שמגדיר כמה משימות יכולות לרוץ במקביל.