简述
Promise构造函数:
Promise(resolve, reject):创建一个新的Promise实例。实例方法:
.then(onFulfilled, onRejected):当Promise状态变为fulfilled时调用onFulfilled函数,当Promise状态变为rejected时调用onRejected函数。.catch(onRejected):当Promise状态变为rejected时调用onRejected函数,相当于.then(null, onRejected)的缩写形式。.finally(onFinally):无论Promise状态最终是fulfilled还是rejected,都会执行onFinally回调函数。静态方法:
Promise.resolve(value):返回一个以给定value解析后的Promise对象。Promise.reject(reason):返回一个带有给定reason拒绝原因的Promise对象。Promise.all(iterable):接收一个可迭代对象,并返回一个Promise,只有当可迭代对象中所有Promise都为fulfilled时才会被解决。Promise.race(iterable):接收一个可迭代对象,并返回一个Promise,只要可迭代对象中的任意一个Promise被解决或拒绝,返回的Promise就会跟随它。Promise.allSettled(iterable):接收一个可迭代对象,并返回一个在所有给定的Promise都已经解决或拒绝后解决的Promise,每个返回的结果都是一个描述状态的对象。Promise.any(iterable):接收一个可迭代对象,并返回一个在可迭代对象中的任何一个Promise被解决时解决的Promise。链式调用方法:
.then().then():可以通过链式调用多个.then(),实现多个异步操作按顺序执行。
function fun(params) {
return new Promise((res) => {
console.log('已经执行')
setTimeout(()=>{
res(1)
},1000)
});
}
fun().then(res=>{
console.log(res)
})
注意的是,
Promise只是延迟了.then的回调,而不是延迟函数执行,所以,当你调用fun函数时,函数体已经执行,只不过1s之后才回调.then。
场景
优化函数回调
- 模拟一个回调函数
function getData(params, callback) {
let urls = {
user: { id: 1, name: "lisi" },
data: { age: 12, addr: 0002 },
};
// 模拟异步操作
setTimeout(() => {
callback(urls[params.url] ? urls[params.url] : null);
}, 1000);
}
这样的例子其实有很多,比如微信小程序里面很多
API都是使用函数回调方式,如果需要调用多次了,就会出现回调地狱现象。
- 优化前
getData({ url: "user" }, (user) => {
console.log("用户:", user);
getData({ url: "data" }, (data) => {
console.log("详情:", data);
});
});
- Promise
// 先使用Promise封装一层
function toData(params) {
return new Promise((res) => {
getData(params, (data) => {
res(data);
});
});
}
// 界面使用
async function initPage() {
try {
let user = await toData({ url: "user" });
console.log("用户:", user);
let data = await toData({ url: "data" });
console.log("详情:", data);
} catch (e) {
console.log("error", e);
}
}
initPage();
这个简单的例子通过将异步操作封装在 Promise 中,优雅地解决了回调地狱问题,并避免了多层嵌套。这种封装方式不仅使代码更加清晰简洁,而且方便了错误处理。
这里使用了
async/await的方式,如果使用Promise的.then函数依旧无需嵌套函数,但我觉得这种方式更为优雅。
同时执行多个异步
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Promise 1 resolved");
}, 2000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Promise 2 resolved");
}, 3000);
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Promise 3 resolved");
}, 1000);
});
Promise.all([promise1, promise2, promise3])
.then((results) => {
// 所有Promise都已经解决后执行
console.log(results); // ["Promise 1 resolved", "Promise 2 resolved", "Promise 3 resolved"]
// 进行下一步处理...
})
.catch((error) => {
// 如果任何一个Promise被拒绝,则会进入这里
console.error(error);
});
需要注意的是,如果传入的
Promise数组中有任意一个Promise被拒绝(rejected),那么Promise.all将立即终止并返回拒绝的原因。只有在所有Promise都解决(即全部成功解决或至少一个被拒绝)后,才会触发then方法。
通过race完成无感刷新
这里我拿web-loading这个插件的源码参考
let feelPromiseResolve: ((value: boolean | PromiseLike<boolean>) => void) | null = null
const loading = (dom: ElementType, options?: OptionsType) => {
// Processing Senseless Loading through rece
const loadingPromise = new Promise<boolean>((res) => {
// If the time of notFeed exceeds the close time, it is considered as an insensitive load
$window.setTimeout(() => {
res(true)
}, op.notFeel)
})
const feelPromise = new Promise<boolean>((res) => {
feelPromiseResolve = res
})
Promise.race([loadingPromise, feelPromise]).then((res) => {
if (res) webLoading.draw(dom)
else {
// Process extended dom
if (op.type !== LOADING_TYPES.DOM) dom.remove()
}
feelPromiseResolve = null
})
}
这里我们可以看到准备两个
Promise,loadingPromise代表的是在notFeel秒内还没调用feelPromiseResolve中的回调就draw绘制,如果notFeel秒内调用feelPromise的回调,那就不会draw绘制,从而达到一个无感刷新效果。
这里为了看更清楚,翻译了下
let feelPromiseResolve = null;
function loading() {
const loadingPromise = new Promise((res) => {
// 2s以内的无感刷新缓冲时间
setTimeout(() => {
res(true);
}, 2000);
});
const feelPromise = new Promise((res) => {
feelPromiseResolve = res;
});
Promise.race([loadingPromise, feelPromise]).then((res) => {
if (res) console.log("刷新中");
else {
console.log("不刷新");
}
feelPromiseResolve = null;
});
}
loading();
// 模拟1s时数据已经加载完成,所以无需显示刷新动画
setTimeout(() => {
if (feelPromiseResolve) feelPromiseResolve(false);
}, 1000);