网站
引入
回调函数是最早的异步处理方式,但嵌套多了就会变成回调地狱。
Promise 是 ES6 引入的异步解决方案,它把异步操作的结果用一个对象来表示。不管操作成功还是失败,都能用统一的方式处理。
Promise 让异步代码从嵌套变成链式调用,读起来更清楚。
正文
定义
定义
Promise 是一个代表异步操作最终结果的对象。它有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。
三种状态
| 状态 | 含义 | 说明 |
|---|---|---|
pending | 进行中 | 初始状态,还没出结果 |
fulfilled | 已成功 | 操作完成,拿到了结果 |
rejected | 已失败 | 操作失败,拿到了错误 |
状态变化只有两种可能:
pending→fulfilledpending→rejected
状态一旦改变,就不会再变。
基本语法
const promise = new Promise(function (resolve, reject) {
let success = true;
if (success) {
resolve("操作成功");
} else {
reject("操作失败");
}
});new Promise()创建一个新的 Promiseresolve是成功时调用的函数,传入结果reject是失败时调用的函数,传入错误信息
.then()
.then() 用来处理成功的结果。
const promise = new Promise(function (resolve, reject) {
resolve("数据加载完成");
});
promise.then(function (result) {
console.log(result);
});输出:数据加载完成
result 就是 resolve() 传入的值。
.catch()
.catch() 用来处理失败的情况。
const promise = new Promise(function (resolve, reject) {
reject("网络错误");
});
promise.catch(function (error) {
console.log(error);
});输出:网络错误
error 就是 reject() 传入的值。
.finally()
.finally() 无论成功还是失败都会执行。
const promise = new Promise(function (resolve, reject) {
resolve("完成");
});
promise
.then(function (result) {
console.log(result);
})
.finally(function () {
console.log("收尾工作");
});输出:
完成
收尾工作
.finally() 常用于关闭加载动画、恢复按钮状态等收尾操作。
链式调用
Promise 最强大的地方是链式调用。
new Promise(function (resolve, reject) {
resolve(1);
})
.then(function (value) {
console.log(value);
return value + 1;
})
.then(function (value) {
console.log(value);
return value + 1;
})
.then(function (value) {
console.log(value);
})
.catch(function (error) {
console.log(error);
});输出:
1
2
3
执行流程:
resolve(1)→ 第一个.then()接收到1return value + 1→ 第二个.then()接收到2return value + 1→ 第三个.then()接收到3- 没有错误,
.catch()不执行
每个 .then() 的返回值会传给下一个 .then()。
链式调用中的错误处理
new Promise(function (resolve, reject) {
resolve(1);
})
.then(function (value) {
console.log(value);
throw new Error("出错了");
})
.then(function (value) {
console.log("不会执行");
})
.catch(function (error) {
console.log(error.message);
});输出:
1
出错了
链式调用中任何一步抛出错误,都会跳到 .catch(),后面的 .then() 不再执行。
用 Promise 封装异步操作
把回调风格的代码改成 Promise:
function wait(seconds) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(seconds + " 秒后执行完成");
}, seconds * 1000);
});
}
wait(2).then(function (message) {
console.log(message);
});封装网络请求:
function fetchData(url) {
return new Promise(function (resolve, reject) {
fetch(url)
.then(function (response) {
return response.json();
})
.then(function (data) {
resolve(data);
})
.catch(function (error) {
reject(error);
});
});
}
fetchData("/api/users")
.then(function (data) {
console.log(data);
})
.catch(function (error) {
console.log("请求失败:" + error.message);
});改造回调地狱
之前的回调地狱:
getUser(function (user) {
getOrders(user, function (orders) {
getOrderDetail(orders[0], function (detail) {
console.log(detail);
});
});
});改成 Promise 链式调用:
getUser()
.then(function (user) {
return getOrders(user);
})
.then(function (orders) {
return getOrderDetail(orders[0]);
})
.then(function (detail) {
console.log(detail);
})
.catch(function (error) {
console.log("出错:" + error.message);
});代码变成从上往下平铺,结构清楚很多。
Promise.all()
Promise.all() 同时执行多个 Promise,全部成功才返回结果。
const p1 = new Promise(function (resolve) {
setTimeout(function () {
resolve("结果 1");
}, 1000);
});
const p2 = new Promise(function (resolve) {
setTimeout(function () {
resolve("结果 2");
}, 2000);
});
Promise.all([p1, p2]).then(function (results) {
console.log(results);
});输出(2 秒后):["结果 1", "结果 2"]
如果其中任何一个失败,整个 Promise.all() 就会进入 .catch()。
Promise.race()
Promise.race() 同时执行多个 Promise,最先完成的那个决定结果。
const p1 = new Promise(function (resolve) {
setTimeout(function () {
resolve("p1 赢了");
}, 1000);
});
const p2 = new Promise(function (resolve) {
setTimeout(function () {
resolve("p2 赢了");
}, 3000);
});
Promise.race([p1, p2]).then(function (result) {
console.log(result);
});输出(1 秒后):p1 赢了
常见用法:给请求设置超时。
const request = fetch("/api/data");
const timeout = new Promise(function (resolve, reject) {
setTimeout(function () {
reject("请求超时");
}, 5000);
});
Promise.race([request, timeout])
.then(function (response) {
console.log("请求成功");
})
.catch(function (error) {
console.log(error);
});Promise.any()
Promise.any() 同时执行多个 Promise,只要有一个成功就返回结果。
const p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
reject("p1 失败");
}, 1000);
});
const p2 = new Promise(function (resolve) {
setTimeout(function () {
resolve("p2 成功");
}, 2000);
});
Promise.any([p1, p2]).then(function (result) {
console.log(result);
});输出(2 秒后):p2 成功
只有全部失败才会进入 .catch()。
对比三个方法
| 方法 | 成功条件 | 失败条件 |
|---|---|---|
Promise.all() | 全部成功 | 任何一个失败 |
Promise.race() | 最先完成的成功 | 最先完成的失败 |
Promise.any() | 任何一个成功 | 全部失败 |
常见写法
| 写法 | 作用 |
|---|---|
new Promise(fn) | 创建 Promise |
.then(fn) | 处理成功结果 |
.catch(fn) | 处理失败情况 |
.finally(fn) | 无论成败都执行 |
Promise.all([]) | 等所有 Promise 完成 |
Promise.race([]) | 取最快的 Promise |
Promise.any([]) | 取最先成功的 Promise |
特点
- Promise 有三种状态:
pending、fulfilled、rejected - 状态一旦改变就不会再变
.then()可以链式调用,返回值传给下一个.then().catch()统一处理链式调用中的错误.finally()用于收尾操作Promise.all()适合需要同时执行多个请求的场景
注意事项
resolve和reject只能调用一次,多次调用会被忽略.then()中返回的值会自动包装成 Promise.catch()后面还可以接.then(),用来处理错误后的逻辑Promise.all()中一个失败就全部失败,需要注意处理- 不要忘记写
.catch(),否则未处理的错误会被静默忽略
理解
可以把 Promise 理解为去餐厅点餐后拿到的取餐号。
点餐的时候(pending),菜还没做好,但已经拿到一个号了。
菜做好了(fulfilled),叫号去取,拿到食物(resolve 的值)。
如果这道菜卖完了(rejected),叫号告诉你没了(reject 的值)。
链式调用就像连续点了好几道菜:第一道上了才能点第二道,第二道上了才能点第三道。
Promise.all() 就像同时点了三道菜,等全部上齐了才能开吃。
Promise.race() 就像同时点了三道菜,哪个先上就先吃哪个。
引出
Promise 解决了回调地狱的问题,但链式调用写多了还是有点长。
JS async await 是更进一步的语法糖,可以让异步代码看起来和同步代码一样简洁。
在实际开发中,Promise 经常和 JS 网络请求 配合使用,fetch() 本身就返回一个 Promise。