promise 规范及应用
Promises / PromiseA+ 规范
promise
:一个标准、规范- 三种状态:
pending
、fulfilled
、reject
,状态一旦改变不允许再修改
js
let promise_0 = new Promise((resolve) => {
setTimeout(() => {
resolve("promise_0");
}, 100);
});
let promise_1 = Promise.resolve(promise_0);
// promise_1 会在 promise_0 fulfilled时,自动以同样的值 fulfilled
let thenable = {
then: function (resolve, reject) {
setTimeout(() => resolve("success"), 100);
},
};
let promise_2 = Promise.resolve(thenable);
// promise_2 最终 fulfilled,值为 "success"
-
状态
- Promise 必须从 pending 转为 fulfilled 或 rejected,且一旦转换之后,状态和值(或原因)就固定下来,不会再变
- 例如:一个异步操作,在成功时调用 resolve 并传入数据,在出错时候调用 reject 并传入错误原因
-
then 方法
-
then 接受两个回调,分别用于处理成功和失败
-
如果回调返回值,则将值传递给下一个 then,如果抛出异常,则下一个 then 会接收到拒绝原因
jssomeAsyncOperation().then((value) => { console.info("success", value); return value + 1; }).then((value) => { console.info("链式调用", value); }).catch((reason) => { console.error("error", reason })
-
-
Promise 解析过程
- 如果一个 then 回调返回了另一个 Promise,那么当前 Promise 就会 "跟随"这个返回的 Promise 状态,并且采用它的值
传统异步任务的处理
-
回调函数
-
一个简单的请求:
- 调用一个函数,这个函数中返回网络请求
- 发送网络请求成功,就告知调用者成功,并返回数据
- 发送网络请求失败,就告知调用者失败,并返回失败信息
jsfunction request(cb) { // 模拟发送网络请求 let flag = Math.random() > 0.5 ? true : false; setTimeout(() => { cb(flag ? "success" : "fail" },500) } // ... console.info("request: 发起请求") request((status,msg)=>{ console.info("request: ",status,msg) }) // 回调函数 request((s1,m1)=>{ // 业务处理逻辑1 request((s2,m2)=>{ // 业务处理逻辑2 request((s3,m3)=>{ // 业务处理逻辑3 console.info("request: ",s3,m3) }) })
-
-
回调地狱
- 如果是自己封装的请求方法,必须设计好规范,如果使用他人库,则必须要通过查看文档或源码才知道如何使用这个函数
- 及其容易出现回调地狱
Promise
- 什么是 Promise
-
是一个类,用来封装异步操作并获取其成功或失败的结果
-
当通过 new 创建 Promise 实例,需要传入一个回调函数,我们称之为 executor
- 这个回调函数会被立即执行,并传入两个参数 resolve 和 reject
- 当调用 resolve 回调函数时,会执行 Promise 对象的 then 方法传入的回调
- 当调用 reject 回调函数时,会执行 Promise 对象的 catch 方法传入的回调
-
Promise 是一个状态机,分为 pending、fulfilled、rejected 三个状态
- pending:初始状态,执行了 executor 后,处于该状态
- fulfilled:成功状态,调用 resolve() 后,Promise 的状态变为 fulfilled,且无法再改变
- rejected:失败状态,调用 reject() 后,Promise 的状态变为 rejected,且无法再改变
js
function request(cb) {
// 模拟发送网络请求
let flag = Math.random() > 0.5 ? true : false;
return new Promise((resolve, reject) => {
setTimeout(() => {
if (flag) {
resolve("success");
return;
}
reject("error");
});
}, 500);
}
console.info("request: 发起请求");
request().then(
(msg) => console.info("request: ", msg),
(err) => console.error("request: ", err)
);
- resolve 的参数
- 如果传入的是普通的值或者对象,则会传递到 then 的参数中
- 如果传入的是一个 Promise,那么当前的 Promise 状态会由传入的 Promise 决定
js
const newPromise = new Promise((resolve, reject) => {
resolve("success");
});
new Promise((resolve, reject) => {
// 当前 Promise 的状态由 newPromise 决定
resolve(newPromise);
})
.then((res) => console.info(res)) // success
.catch((err) => console.error(err));
- 如果传入的是一个对象,并且该对象实现了 then 方法 (thenable),也会执行该 then 方法,并由该 then 方法决定后续的状态
js
new Promise((resolve, reject) => {
resolve({
then(resolve, reject) {
reject("error");
},
});
}).then(
(res) => console.info(res),
(err) => console.error(err) // error
);
- Promise 的实例方法
-
then 方法:通过 then 方法可以对 Promise 的 resolve 进行处理,then 方法的返回值是一个 Promise 实例
- 多次调用 then 方法:同一个 Promise 实例可以多次调用 then 方法,当 Promise 中 resolve 被回调时,所有 then 方法传入的传入的函数都会被调用
js
const promise_0 = new Promise((resolve, reject) => {
resolve("success");
});
// 同时调用
promise_0.then((res) => console.info(res));
promise_0.then((res) => console.info(res));
promise_0.then((res) => console.info(res));
-
then 方法传入的回调函数可以有返回值
- 如果返回的是普通值,那么这个普通值作为一个新的 Promise 的 resolve 的值
js
const promise_1 = new Promise((resolve, reject) => {
resolve("success");
});
promise_1.then(() => "then").then((res) => console.info(res)); // then
// promise_1.then(() => "then") 等价于
promise_1.then(() => {
return new Promise((resolve) => {
resolve("then");
});
});
- 如果返回的是 Promise,那么就可以再次调用 then 方法
- 如果返回的是一个对象,并且该对象实现了 thenable,该 then 函数有两个参数 resolve,reject,则 resolve 会传递给下一个 Promise
js
const promise_2 = new Promise((resolve, reject) => {
resolve("success");
});
promise_2
.then(() => {
return {
then(resolve) {
return resolve("then"); // resolve 传递给下一个 Promise
},
};
})
.then((res) => console.info(res)); // then
-
catch 方法
- 除了 then 方法的第二个参数来捕获 reject 之外, catch 方法用于处理 Promise 的 reject,catch 方法返回的也是一个 Promise 实例
js
const promise_3 = new Promise((resolve, reject) => {
resolve("success");
});
promise_3.then((undefined, err) => {
// 打印 err
console.error(err);
});
// 这种写法不太符合 `promise/A+ 规范`
promise_3.catch((err) => {
// 打印 err
console.error(err);
});
// 符合规范的写法
promise_3.then(
() => {},
(err) => {
console.error(err);
}
);
-
catch 方法也是可以多次调用的,只要 Promise 实例的状态是 reject,那么就会调用 catch 方法
-
finally 方法
- 无论一个 Promise 实例是 fulfilled 还是 rejected,finally 方法都会被调用。finally 方法不接收参数,返回的也是一个 Promise 实例
js
const promise_4 = new Promise((resolve, reject) => {
resolve("success");
});
promise_4
.then(() => {})
.catch((err) => console.error(err))
.finally(() => console.info("finally code execute"));
- Promise 的类方法
- resolve 方法
js
const foo = { name: "Fa-ce" };
function bar(obj) {
return new Promise((resolve) => {
resolve(obj);
});
}
bar(foo).then((res) => console.info(res)); // { name: 'Fa-ce' }
-
resolve 参数:
- 参数本身是 Promise
- 参数是原始值/对象
- 参数是一个 thenable
- reject 方法
- all 方法
- race 方法
- allSettled 方法
- any 方法
-
all 方法:接收一个 Promise[],返回一个 Promise 实例,当所有的 Promise 执行完毕且都是 fulfilled 状态时,该实例的状态才会变成 fulfilled,只要队列中有一个实例的状态是 rejected,那么该实例的状态就会变成 rejected
- 如果 Promise 队列中所有的实例状态都是 fulfilled,那么 Promise.all() 方法返回的实例状态就会变成 fulfilled,并且 then() 的参数使用给数组,按照顺序放置队列中每个 Promise 成功后的结果
js
let i = 0;
function genPromise() {
return new Promise((resolve) => {
resolve(`success${i++}`);
});
}
const promiseArr = [genPromise(), genPromise(), genPromise()];
Promise.all(promiseArr).then((res) => console.info(res)); // ['success0', 'success1', 'success2']
- 如果队列中 Promise 实例有一个是 rejected,那么 Promise.all() 方法返回的实例状态就会变成 rejected,并且 reject() 的参数使用给第一个 rejected 的结果
js
let i = 0;
function genPromise() {
return new Promise((resolve) => {
resolve(`success${i++}`);
});
}
const promiseArr = [
genPromise(),
new Promise((resolve, reject) => reject("error1")),
new Promise((resolve, reject) => reject("error2")),
genPromise(),
];
Promise.all(promiseArr).catch((err) => console.info(err)); // error1
-
allSettled 方法:该方法返回的 Promise 实例,会在所有 Promise 实例执行完毕后,状态变成 fulfilled,并且只会是 fulfilled
- 无论队列中的 Promise 实例的状态如何,都能取到结果
- 打印的结果,会包含状态以及值/原因
js
let i = 0;
function genPromise() {
return new Promise((resolve) => {
resolve(`success${i++}`);
});
}
const promiseArr = [
genPromise(),
new Promise((resolve, reject) => reject("error1")),
new Promise((resolve, reject) => reject("error2")),
genPromise(),
];
Promise.allSettled(promiseArr)
.then((res) => console.info(res))
.catch((err) => console.info(err));
-
race 方法
Promise.race()
方法接收一个Promise
数组,返回一个Promise
实例,只要数组中的某个Promise
实例状态改变,那么返回的Promise
实例状态就会改变,并且使用第一个改变状态的Promise
实例的值/原因- 返回最先执行完的
Promise
实例
js
const promiseArr = [
new Promise((resolve, reject) => {
setTimeout(() => {
resolve("success");
}, 1000);
}),
new Promise((resolve, reject) => {
setTimeout(() => {
reject("error");
}, 1500);
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve("success");
}, 600);
}),
];
Promise.race(promiseArr)
.then((res) => console.info(res)) // success
.catch((err) => console.info(err));
-
any 方法
Promise.any()
方法会等待一个fulfilled
状态,才会决定返回的Promise
实例的状态- 如果队列中所有的实例都是
reject
,那么也需要等所有执行完毕后才返回,并且返回的实例状态是rejected
,并且使用第一个reject
的值/原因
js
const promiseArr = [
new Promise((resolve, reject) => {
setTimeout(() => {
resolve("success");
// reject("reject-0");
}, 1000);
}),
new Promise((resolve, reject) => {
setTimeout(() => {
reject("error");
}, 1500);
}),
new Promise((resolve, reject) => {
setTimeout(() => {
// resolve("success");
reject("reject_2");
}, 600);
}),
];
Promise.any(promiseArr)
.then((res) => console.info(res))
.catch((err) => console.info(err));
// 遇到第一个 fulfilled时,转变返回的 Promise 实例状态
// 如果所有都是 rejected,那么只有所有执行完毕后,才会返回
Promise 回调和 async/await
js
Promise.resolve("green").then((result) => {
console.log(result);
Promise.resolve("red").then((result) => {
console.log(result);
Promise.resolve("yellow").then((result) => {
console.log(result);
});
});
});
js
async () => {
await Promise.resolve("green").then((res) => {
console.log(res);
});
await Promise.resolve("red").then((res) => {
console.log(res);
});
await Promise.resolve("yellow").then((res) => {
console.log(res);
});
};
// 浏览器不直接支持 async/await,需要 babel 转译
- async 是 function 的一个前缀,只有 async 函数中才能使用 await 语法
- async 函数是一个 Promise 对象,有无 resolve 取决于有无函数中 return 值
- await 后边跟得是一个 Promise 对象,如果不是,则会包裹一层 Promise.resolve(),将其转化为 Promise 对象
await 只能在 async 函数中使用,不能在普通函数中使用,它会暂停函数执行, 直到 等待的 Promise 成功或者失败
js
async function fetchData() {
return "Data fetched successfully";
}
async function showData() {
const data = await fetchData();
console.log(data);
}
/* ************ */
async function processData() {
try {
const data = await fetchData();
console.log(data);
} catch (err) {
console.log(err);
}
}
js
async function processSequentAll() {
const p1 = await fetchData1();
const p2 = await fetchData2(data2);
const p3 = await fetchData3(data3);
}
// 或者
async function processSequentAll() {
const p1 = fetchData1();
const p2 = fetchData2(data2);
const p3 = fetchData3(data3);
const result = await Promise.all([p1, p2, p3]);
console.log(result);
}
- 从 async/await 到 promise
js
function fetchDataAsPromise() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("data fetched successfully");
}, 1000);
});
}
// 从 promise 到 async
async function useFetchDataAsPromise() {
const data = await fetchDataAsPromise();
console.log(data);
}
useFetchDataAsPromise();