3.1 Creating Promise Chains
Promise chains let you perform several async operations in sequence, passing the result of one operation to the next one. This makes your code more readable and manageable. Plus, promises offer powerful error-handling capabilities, making it possible to build reliable and fault-tolerant applications.
Main Concept
A promise chain is created by returning a new promise from the then
method. Each then
method returns a new promise, allowing you to build sequential async operations.
Simple Promise Chain Example
In this example, each then
method performs its operation and passes the result to the next then
method.
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Step 1 completed');
}, 1000);
});
promise
.then((result) => {
console.log(result); // Outputs: Step 1 completed
return 'Step 2 completed';
})
.then((result) => {
console.log(result); // Outputs: Step 2 completed
return 'Step 3 completed';
})
.then((result) => {
console.log(result); // Outputs: Step 3 completed
})
.catch((error) => {
console.error(error);
});
Example with Async Operations
In this example, each then
method waits for the async operation to complete before proceeding to the next step:
function asyncOperation(step) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${step} completed`);
}, 1000);
});
}
asyncOperation('Step 1')
.then((result) => {
console.log(result); // Outputs: Step 1 completed
return asyncOperation('Step 2');
})
.then((result) => {
console.log(result); // Outputs: Step 2 completed
return asyncOperation('Step 3');
})
.then((result) => {
console.log(result); // Outputs: Step 3 completed
})
.catch((error) => {
console.error(error);
});
3.2 Error Handling in Promise Chains
The catch
method is used to handle errors in a promise chain. If an error occurs in any of the promises, it is passed to the nearest catch
method. This allows centralized error handling, improving code readability and maintainability.
Error Handling Example
In this example, an error in Step 2
is caught by the catch
method, and subsequent then
methods do not execute:
function asyncOperation(step, shouldFail = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldFail) {
reject(`${step} failed`);
} else {
resolve(`${step} completed`);
}
}, 1000);
});
}
asyncOperation('Step 1')
.then((result) => {
console.log(result);
return asyncOperation('Step 2', true); // This operation will fail
})
.then((result) => {
console.log(result); // This code will not be executed
return asyncOperation('Step 3');
})
.then((result) => {
console.log(result); // This code will not be executed
})
.catch((error) => {
console.error('Error:', error); // Outputs: Error: Step 2 failed
});
Handling Errors in Specific Steps
Sometimes, you might need to handle errors in specific steps without stopping the entire chain. You can use nested then
and catch
methods for this.
Error Handling in Specific Steps Example
In this example, an error in Step 2
is handled locally, and the chain continues with a recovered value:
function asyncOperation(step, shouldFail = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldFail) {
reject(`${step} failed`);
} else {
resolve(`${step} completed`);
}
}, 1000);
});
}
asyncOperation('Step 1')
.then((result) => {
console.log(result);
return asyncOperation('Step 2', true).catch((error) => {
console.warn('Handled error in Step 2:', error);
return 'Recovered from Step 2 error';
});
})
.then((result) => {
console.log(result); // Outputs: Recovered from Step 2 error
return asyncOperation('Step 3');
})
.then((result) => {
console.log(result); // Outputs: Step 3 completed
})
.catch((error) => {
console.error('Error:', error); // This code will not be executed
});
3.3 Using finally
The finally
method executes code regardless of whether the promise was resolved successfully or with an error. This is useful for performing cleanup operations or freeing up resources.
Using finally Example:
function asyncOperation(step, shouldFail = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldFail) {
reject(`${step} failed`);
} else {
resolve(`${step} completed`);
}
}, 1000);
});
}
asyncOperation('Step 1')
.then((result) => {
console.log(result);
return asyncOperation('Step 2');
})
.then((result) => {
console.log(result);
return asyncOperation('Step 3');
})
.catch((error) => {
console.error('Error:', error);
})
.finally(() => {
console.log('All operations completed'); // Will be executed in any case
});
In this example, the finally
method runs no matter what, regardless of whether an error occurred.
GO TO FULL VERSION