简介
说明
本文用示例介绍JavaScript的Promise。它是ES6新增的特性。
Promise的用途
- 用来异步编程
- 解决回调地狱(也称为:厄运金字塔)
- 在异步调用中再次进行异步回调,如果嵌套的层级太多,会导致难以维护。
Promise 的三种状态
- pending:等待(初始状态)
- 在 resolve 被调用时变为 "fulfilled"。
- 在reject 被调用时变为 "rejected" 。
- fulfilled:已完成
- rejected:已失败。(已拒绝)
状态变为resolved 或 rejected 之后,就不会再次改变。这两个状态被称为 “settled”(已定型)。
用法简介
简述
Promise 都有这些内容:构造函数、对象的实例方法、静态方法
- 1个构造函数:new Promise
- 3个对象的实例方法:.then 和 .catch 和 finally(不常用)
- 5个静态方法:Promise.all、Promise.allSettled、Promise.race、Promise.resolve、Promise.reject
then与catch
.catch(f) 调用是 .then(null, f) 的完全的模拟,它只是一个简写形式。
then与catch只用一个即可。一般用then,因为它可以处理成功(fulfilled)与失败(rejected),而catch只能处理失败(rejected)。
下边程序中,会进行reject,后续then会执行,.catch不会执行,finally会执行。
let promise = new Promise(function (resolve, reject) {
setTimeout(() => {
reject("定时任务结果(reject)");
}, 1000);
});
promise
.then(
result => console.log("成功(then):" + result),
error => console.log("错误(then):" + error)
)
.catch(
error => console.log("错误(catch):" + error)
)
.finally(
() => console.log("运行结束(finally)")
);
结果
错误(then):定时任务结果(reject)
运行结束(finally)
resolve与reject
resolve方法:
通过回调里的 resolve(data) 将这个状态标记为 fulfilled,然后进行下一步 then((data)=>{//do something})。resolve 里的data就是你要传入 then 的data。
reject方法:
通过回调里的 reject(data) 将这个状态标记为 rejected,然后进行下一步 then((data1, data2)=>{//do something})。resolve 里的data就是你要传入 then 的data2。
executor 只能调用一个 resolve 或一个 reject ,所有其他的 resolve 和 reject 的调用都会被忽略:
let promise = new Promise(function(resolve, reject) {
resolve("done");
reject(new Error("…")); // 被忽略
setTimeout(() => resolve("…")); // 被忽略
});
示例:异步编程
示例1:resolve(成功)
let promise = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve("定时任务结果(resolve)");
}, 1000);
});
promise
.then(
result => console.log("成功(then):" + result),
error => console.log("错误(then):" + error)
)
.finally(
() => console.log("运行结束(finally)")
);
结果
成功(then):定时任务结果(resolve)
运行结束(finally)
示例2:reject(失败)
let promise = new Promise(function (resolve, reject) {
setTimeout(() => {
reject("定时任务结果(reject)");
}, 1000);
});
promise
.then(
result => console.log("成功(then):" + result),
error => console.log("错误(then):" + error)
)
.finally(
() => console.log("运行结束(finally)")
);
结果
错误(then):定时任务结果(reject)
运行结束(finally)
示例:解决回调地狱
什么是回调地域?
在异步调用中再次进行异步回调,如果嵌套的层级太多,会导致难以维护。这就是回调地狱。
例如:使用ajax请求url,成功后再请求url1,成功后再请求url2,代码会是下边这个样子:
ajax(url, () => {
// 处理逻辑
ajax(url1, () => {
// 处理逻辑
ajax(url2, () => {
// 处理逻辑
})
})
})
为了方便演示,本处采用定时任务的方式进行演示。
需求:每1秒钟输出一次,分别输出:first、second、third、end。
复现回调地狱
let fn = function (order, callback) {
setTimeout(function () {
console.log(order);
callback();
}, 1000);
}
fn("first", function () {
fn("second", function () {
fn("third", function () {
console.log("end");
});
});
});
结果
first
second
third
end
Promise解决回调地狱
let fn = function (order) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(order);
//异步操作执行完后执行 resolve() 函数
resolve();
}, 1000);
});
}
fn("first")
.then(function () {
//仍然返回一个 Promise 对象
return fn("second");
})
.then(function () {
return fn("third");
})
.then(function () {
console.log('end');
})
结果
first
second
third
end
async + await 解决回调地狱
async + await是对promise的进一步封装。
let fnTimeOut = function (order) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(order);
//异步操作执行完后执行 resolve() 函数
resolve();
}, 1000);
}
);
};
async function fn() {
let first = await fnTimeOut("first");
let second = await fnTimeOut("second");
let third = await fnTimeOut("third");
console.log("end");
}
fn();
结果
first
second
third
end