一、为什么需要 promise?
1、回调地狱(Callback Hell):在传统的 JavaScript 中,为了处理异步操作,通常会通过回调函数来实现。当多个异步操作依赖于上一个操作的结果时,就会出现多层嵌套的回调,导致代码可读性差、难以维护。
2、缺乏错误处理机制:在回调函数中,错误处理不够方便和统一。每个回调函数都需要单独处理可能发生的错误,容易出现遗漏或者混乱。
3、难以实现并行和串行执行:在传统的异步编程模式中,要实现并行执行多个异步操作或者按特定顺序执行异步操作较为复杂。
二、什么是 promise?
1、基本概念
Promises/A+规范网址: Promises/A+
Promise 是 JavaScript 中用于处理异步操作的一种解决方案。它是一个对象,代表了一个异步操作的最终完成(或失败)及其结果的表示。Promise 可以让我们更容易地进行异步编程,并避免了回调地狱的情况。
具体来说,Promise 有以下几个重要特点:
-
状态(State):一个 Promise 可以处于三种状态之一:进行中(pending)、已成功(fulfilled)和已失败(rejected)。当一个 Promise 转为 fulfilled 或 rejected 状态时,就称为 settled 状态,此后状态不会再改变。
-
值(Value):在 fulfilled 状态时,Promise 对象会保存一个值,表示异步操作的结果。在 rejected 状态时,会保存一个原因,表示异步操作失败的原因。
-
then() 方法:Promise 对象提供了 then() 方法,用于指定当异步操作成功(fulfilled)或失败(rejected)时的回调函数。then() 方法接受两个回调函数作为参数,第一个参数用于处理成功的情况,第二个参数用于处理失败的情况。then() 方法返回一个新的 Promise 对象,可以链式调用多个 then() 方法。
-
catch() 方法:Promise 对象提供了 catch() 方法,用于捕获异步操作中的错误。相当于 then(null, onRejected) 的简便写法。
-
finally() 方法:Promise 对象提供了 finally() 方法,无论 Promise 最终状态如何,都会执行 finally() 中的回调函数。通常用于清理工作或资源释放等操作。
-
静态方法:Promise 还提供了一些静态方法,如 Promise.resolve()、Promise.reject()、Promise.all()、Promise.race() 等,用于快速创建和处理多个 Promise 对象的情况。
总的来说,Promise 是一种处理异步操作的优雅方式,通过其简洁的 API 和链式调用的特性,使得异步代码更易于理解和维护。在实际开发中,Promise 是非常常用且强大的工具,有助于提高代码的可读性和可维护性。
2、实际应用
// 模拟异步操作函数,返回一个 Promise 对象
function asyncOperation(value, delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`Async operation completed: ${value}`);
resolve(value);
}, delay);
});
}
// 解决回调地狱:使用 Promise 链式调用来依次执行异步操作
asyncOperation(1, 1000)
.then((result1) => {
return asyncOperation(result1 + 1, 1500);
})
.then((result2) => {
return asyncOperation(result2 + 1, 2000);
})
.then((result3) => {
console.log('Final result:', result3);
})
.catch((error) => {
console.error('An error occurred:', error);
});
// 并行执行异步操作:使用 Promise.all() 实现多个异步操作并行执行
Promise.all([
asyncOperation('A', 1500),
asyncOperation('B', 1000),
asyncOperation('C', 2000)
]).then((results) => {
console.log('All operations completed in parallel:', results);
}).catch((error) => {
console.error('An error occurred in parallel operations:', error);
});
// 串行执行异步操作:使用 Promise.then() 实现多个异步操作依次执行
asyncOperation('X', 1000)
.then(() => asyncOperation('Y', 1500))
.then(() => asyncOperation('Z', 2000))
.then(() => {
console.log('Sequential operations completed');
})
.catch((error) => {
console.error('An error occurred in sequential operations:', error);
});
三、手写一个promise以及实际应用
1、手写一个promise
class MyPromise {
constructor(executor) {
// 初始状态为 pending
this.state = 'pending';
// 存储成功结果和失败原因
this.value = undefined;
this.reason = undefined;
// 存储成功和失败的回调函数
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
// resolve 函数用于将 Promise 状态设为 fulfilled,并执行成功回调函数
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
// 依次执行成功回调函数
this.onFulfilledCallbacks.forEach((callback) => callback(this.value));
}
};
// reject 函数用于将 Promise 状态设为 rejected,并执行失败回调函数
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
// 依次执行失败回调函数
this.onRejectedCallbacks.forEach((callback) => callback(this.reason));
}
};
try {
// 执行用户传入的 executor 函数,并传入 resolve 和 reject 函数作为参数
executor(resolve, reject);
} catch (error) {
// 如果在执行过程中抛出异常,则将 Promise 状态设为 rejected
reject(error);
}
}
then(onFulfilled, onRejected) {
// 创建一个新的 Promise 对象
const newPromise = new MyPromise((resolve, reject) => {
// 用于处理成功状态的回调函数
const handleFulfilled = (value) => {
try {
// 执行用户传入的 onFulfilled 回调函数,并将返回值传递给下一个 Promise
const result = onFulfilled(value);
resolve(result);
} catch (error) {
// 如果执行过程中抛出异常,则将 Promise 状态设为 rejected
reject(error);
}
};
// 用于处理失败状态的回调函数
const handleRejected = (reason) => {
try {
// 执行用户传入的 onRejected 回调函数,并将返回值传递给下一个 Promise
const result = onRejected(reason);
resolve(result);
} catch (error) {
// 如果执行过程中抛出异常,则将 Promise 状态设为 rejected
reject(error);
}
};
if (this.state === 'pending') {
// 如果当前 Promise 还处于 pending 状态,则将回调函数存储起来
this.onFulfilledCallbacks.push(handleFulfilled);
this.onRejectedCallbacks.push(handleRejected);
} else if (this.state === 'fulfilled') {
// 如果当前 Promise 已经处于 fulfilled 状态,则使用 setTimeout 异步执行成功回调函数
setTimeout(() => {
handleFulfilled(this.value);
});
} else if (this.state === 'rejected') {
// 如果当前 Promise 已经处于 rejected 状态,则使用 setTimeout 异步执行失败回调函数
setTimeout(() => {
handleRejected(this.reason);
});
}
});
return newPromise;
}
catch(onRejected) {
// catch 方法实际上是 then 方法的一个特殊情况,只传入了失败回调函数
return this.then(undefined, onRejected);
}
static resolve(value) {
// 静态方法 resolve 创建一个已经被解决的 Promise 对象,并返回它
return new MyPromise((resolve) => {
resolve(value);
});
}
static reject(reason) {
// 静态方法 reject 创建一个已经被拒绝的 Promise 对象,并返回它
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
static all(promises) {
// 静态方法 all 接收一个 Promise 对象数组,并在所有 Promise 对象都变为 fulfilled 状态后返回一个新的 Promise 对象
return new MyPromise((resolve, reject) => {
const results = [];
let fulfilledCount = 0;
promises.forEach((promise, index) => {
promise.then((value) => {
// 将每个 Promise 的结果存储起来
results[index] = value;
fulfilledCount++;
if (fulfilledCount === promises.length) {
// 如果所有 Promise 都已经完成,则将新的 Promise 对象设为 fulfilled 状态,并将结果返回
resolve(results);
}
}).catch(reject);
});
});
}
static race(promises) {
// 静态方法 race 接收一个 Promise 对象数组,并返回其中最先变为 fulfilled 或 rejected 状态的 Promise 对象
return new MyPromise((resolve, reject) => {
promises.forEach((promise) => {
// 只处理第一个完成的 Promise 对象,无论是 fulfilled 还是 rejected
promise.then(resolve).catch(reject);
});
});
}
}
2、实际应用
// 创建一个新的 Promise 对象并执行异步操作
const myPromise = new MyPromise((resolve, reject) => {
setTimeout(() => {
const randomNumber = Math.random();
if (randomNumber > 0.5) {
resolve(randomNumber);
} else {
reject('Random number is less than 0.5');
}
}, 1000);
});
// 使用 then 方法处理 Promise 的成功和失败情况
myPromise.then(
(value) => {
console.log('Resolved:', value);
},
(reason) => {
console.error('Rejected:', reason);
}
);
// 使用 catch 方法捕获 Promise 的失败情况
myPromise.catch((reason) => {
console.error('Caught:', reason);
});
// 使用 finally 方法在 Promise 执行结束后执行特定的操作
myPromise.finally(() => {
console.log('Finally block executed');
});
// 使用静态方法 all 来处理多个 Promise 实例并等待它们全部完成
const promises = [promise1, promise2, promise3];
MyPromise.all(promises).then((results) => {
console.log('All promises resolved:', results);
}).catch((reason) => {
console.error('One or more promises rejected:', reason);
});
// 使用静态方法 race 来处理多个 Promise 实例,并只处理第一个完成的 Promise
const promises = [promise1, promise2, promise3];
MyPromise.race(promises).then((value) => {
console.log('First promise resolved:', value);
}).catch((reason) => {
console.error('First promise rejected:', reason);
});
四、async 和 await
1、定义
async 函数用于声明一个异步函数,函数内部可以包含异步操作,并且会隐式地返回一个 Promise 对象。
await 关键字用于暂停当前 async 函数的执行,等待 Promise 对象的状态变为 resolved 后继续执行,并且可以获取 Promise 返回的值。
2、使用场景
(1)当需要处理异步操作时,例如网络请求、文件读写、定时器等,可以使用 async/await 来简化异步代码的书写。
async function fetchUserData() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
if (!response.ok) {
throw new Error('Failed to fetch user data');
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
fetchUserData();
(2)在处理多个异步操作时,使用 async/await 可以更清晰地表达代码逻辑,避免回调地狱(callback hell)的情况。
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function performMultipleAsyncOperations() {
try {
console.log('Starting multiple async operations...');
// 第一个异步操作
await delay(1000);
console.log('First async operation completed');
// 第二个异步操作
await delay(1500);
console.log('Second async operation completed');
// 第三个异步操作
await delay(2000);
console.log('Third async operation completed');
console.log('All async operations completed');
} catch (error) {
console.error(error);
}
}
performMultipleAsyncOperations();
3、与promise之间的关系
async/await 是基于 Promise 的封装,可以认为 async 函数是 Promise 的一种更高级的语法糖,使异步代码更加易读和易写。
在 async 函数内部,可以使用 await 关键字来等待一个 Promise 对象的状态变化,相比于使用 then() 方法处理 Promise,async/await 更加直观和简洁。