Skip to main content
โšก Calmops

Promises: Creation, Chaining, Resolution

Promises: Creation, Chaining, Resolution

Promises provide a cleaner way to handle asynchronous operations compared to callbacks.

What is a Promise?

A Promise represents the eventual completion (or failure) of an asynchronous operation:

const promise = new Promise((resolve, reject) => {
    // Asynchronous operation
    if (success) {
        resolve(value); // Fulfilled
    } else {
        reject(error); // Rejected
    }
});

Promise States

A Promise has three states:

  1. Pending: Initial state, operation hasn’t completed
  2. Fulfilled: Operation completed successfully
  3. Rejected: Operation failed
// Pending
const promise = new Promise((resolve, reject) => {
    // Still pending
});

// Fulfilled
const fulfilled = new Promise((resolve, reject) => {
    resolve("Success!");
});

// Rejected
const rejected = new Promise((resolve, reject) => {
    reject(new Error("Failed!"));
});

Creating Promises

Basic Promise

function fetchUser(userId) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (userId > 0) {
                resolve({ id: userId, name: "Alice" });
            } else {
                reject(new Error("Invalid user ID"));
            }
        }, 1000);
    });
}

fetchUser(1)
    .then(user => console.log(user))
    .catch(error => console.error(error));

Resolved/Rejected Promises

// Immediately resolved
const resolved = Promise.resolve("Success");

// Immediately rejected
const rejected = Promise.reject(new Error("Failed"));

// Resolve with value
Promise.resolve(42).then(value => console.log(value)); // 42

Consuming Promises

then() Method

promise.then(
    (value) => {
        // Handle success
        console.log(value);
    },
    (error) => {
        // Handle error (optional)
        console.error(error);
    }
);

catch() Method

promise
    .then(value => console.log(value))
    .catch(error => console.error(error));

finally() Method

promise
    .then(value => console.log(value))
    .catch(error => console.error(error))
    .finally(() => {
        console.log("Operation complete"); // Always runs
    });

Promise Chaining

Chain multiple asynchronous operations:

function getUser(userId) {
    return new Promise((resolve) => {
        setTimeout(() => resolve({ id: userId, name: "Alice" }), 1000);
    });
}

function getOrders(userId) {
    return new Promise((resolve) => {
        setTimeout(() => resolve([{ id: 1, total: 100 }]), 1000);
    });
}

getUser(1)
    .then(user => {
        console.log("User:", user);
        return getOrders(user.id);
    })
    .then(orders => {
        console.log("Orders:", orders);
    })
    .catch(error => console.error(error));

Returning Values

Promise.resolve(5)
    .then(value => {
        console.log(value); // 5
        return value * 2;
    })
    .then(value => {
        console.log(value); // 10
        return value + 3;
    })
    .then(value => {
        console.log(value); // 13
    });

Returning Promises

function step1() {
    return Promise.resolve("Step 1 complete");
}

function step2(result) {
    return new Promise((resolve) => {
        setTimeout(() => resolve(`${result} -> Step 2 complete`), 1000);
    });
}

function step3(result) {
    return Promise.resolve(`${result} -> Step 3 complete`);
}

step1()
    .then(result => step2(result))
    .then(result => step3(result))
    .then(result => console.log(result));

Promise Utilities

Promise.all()

Wait for all promises to resolve:

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve) => setTimeout(() => resolve("foo"), 100));
const promise3 = fetch("https://api.example.com/data").then(r => r.json());

Promise.all([promise1, promise2, promise3])
    .then(values => {
        console.log(values); // [3, "foo", data]
    })
    .catch(error => console.error(error));

Promise.race()

Return first promise to settle:

const promise1 = new Promise((resolve) => setTimeout(() => resolve("First"), 500));
const promise2 = new Promise((resolve) => setTimeout(() => resolve("Second"), 100));

Promise.race([promise1, promise2])
    .then(value => console.log(value)); // "Second"

Promise.allSettled()

Wait for all promises to settle (resolve or reject):

const promises = [
    Promise.resolve(1),
    Promise.reject(new Error("Failed")),
    Promise.resolve(3)
];

Promise.allSettled(promises)
    .then(results => {
        console.log(results);
        // [
        //   { status: "fulfilled", value: 1 },
        //   { status: "rejected", reason: Error },
        //   { status: "fulfilled", value: 3 }
        // ]
    });

Promise.any()

Return first fulfilled promise:

const promises = [
    Promise.reject(new Error("Error 1")),
    Promise.resolve("Success"),
    Promise.reject(new Error("Error 2"))
];

Promise.any(promises)
    .then(value => console.log(value)) // "Success"
    .catch(error => console.error(error));

Error Handling

Catching Errors

function fetchData(url) {
    return fetch(url)
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            return response.json();
        })
        .catch(error => {
            console.error("Fetch error:", error);
            throw error; // Re-throw to propagate
        });
}

Error Propagation

Promise.resolve(1)
    .then(value => {
        throw new Error("Error in step 1");
    })
    .then(value => {
        console.log(value); // Never runs
    })
    .catch(error => {
        console.error(error); // Catches error from step 1
    });

Practical Examples

Retry Logic

function retryPromise(fn, maxRetries = 3) {
    return fn().catch(error => {
        if (maxRetries <= 1) {
            throw error;
        }
        console.log(`Retry ${4 - maxRetries}...`);
        return retryPromise(fn, maxRetries - 1);
    });
}

retryPromise(() => {
    return Math.random() > 0.7
        ? Promise.resolve("Success")
        : Promise.reject(new Error("Failed"));
}, 3)
    .then(result => console.log(result))
    .catch(error => console.error("All retries failed"));

Timeout

function withTimeout(promise, ms) {
    return Promise.race([
        promise,
        new Promise((_, reject) =>
            setTimeout(() => reject(new Error("Timeout")), ms)
        )
    ]);
}

withTimeout(
    fetch("https://api.example.com/data").then(r => r.json()),
    5000
)
    .then(data => console.log(data))
    .catch(error => console.error(error));

Sequential Execution

function sequential(tasks) {
    return tasks.reduce(
        (promise, task) => promise.then(task),
        Promise.resolve()
    );
}

sequential([
    () => {
        console.log("Task 1");
        return new Promise(resolve => setTimeout(resolve, 1000));
    },
    () => {
        console.log("Task 2");
        return new Promise(resolve => setTimeout(resolve, 1000));
    },
    () => {
        console.log("Task 3");
        return Promise.resolve();
    }
])
    .then(() => console.log("All tasks complete"));

Parallel Execution

function parallel(tasks) {
    return Promise.all(tasks.map(task => task()));
}

parallel([
    () => new Promise(resolve => setTimeout(() => resolve("Result 1"), 1000)),
    () => new Promise(resolve => setTimeout(() => resolve("Result 2"), 500)),
    () => new Promise(resolve => setTimeout(() => resolve("Result 3"), 1500))
])
    .then(results => console.log(results));

Converting Callbacks to Promises

// Callback-based function
function readFile(filename, callback) {
    setTimeout(() => {
        if (filename) {
            callback(null, "File contents");
        } else {
            callback(new Error("Filename required"));
        }
    }, 1000);
}

// Convert to Promise
function readFilePromise(filename) {
    return new Promise((resolve, reject) => {
        readFile(filename, (error, data) => {
            if (error) {
                reject(error);
            } else {
                resolve(data);
            }
        });
    });
}

readFilePromise("file.txt")
    .then(data => console.log(data))
    .catch(error => console.error(error));

Summary

  • Promise: represents eventual completion of async operation
  • States: pending, fulfilled, rejected
  • then(): handle success
  • catch(): handle error
  • finally(): always execute
  • Chaining: return promises to chain operations
  • Promise.all(): wait for all promises
  • Promise.race(): return first promise
  • Promise.allSettled(): wait for all to settle
  • Promise.any(): return first fulfilled

Official Documentation

Next Steps

  1. Async/Await: Modern Asynchronous Programming
  2. Fetch API: Making HTTP Requests
  3. Error Handling with Promises and Async/Await

Comments