promise 规范及应用
Promises / PromiseA+ 规范
PromiseA+ 规范
-
promise
:一个标准、规范
- 三种状态:
pending
、fulfilled
、reject
,状态一旦改变不允许再修改
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 会接收到拒绝原因
someAsyncOperation().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 状态,并且采用它的值
传统异步任务的处理
-
回调函数
-
一个简单的请求:
- 调用一个函数,这个函数中返回网络请求
- 发送网络请求成功,就告知调用者成功,并返回数据
- 发送网络请求失败,就告知调用者失败,并返回失败信息
function 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,且无法再改变
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 决定
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 方法决定后续的状态
new Promise((resolve, reject) => {
resolve({
then(resolve, reject) {
reject("error");
},
});
}).then(
(res) => console.info(res),
(err) => console.error(err) // error
);
- Promise 的实例方法
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 的值
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
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 实例
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);
}
);
const promise_4 = new Promise((resolve, reject) => {
resolve("success");
});
promise_4
.then(() => {})
.catch((err) => console.error(err))
.finally(() => console.info("finally code execute"));
- resolve 方法
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 方法
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 的结果
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
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
实例
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
的值/原因
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
Promise.resolve("green").then((result) => {
console.log(result);
Promise.resolve("red").then((result) => {
console.log(result);
Promise.resolve("yellow").then((result) => {
console.log(result);
});
});
});
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 成功或者失败
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);
}
}
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);
}
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();