网站首页 > 文章精选 正文
在JavaScript中,Promise是一种解决异步编程问题的方式,一个Promise对象,代表了一个将要在本次操作完成后立即、稍后或从未实现的返回值。那么如何更优雅、高效的使用Promise呢?
并发控制
使用Promise.all 可以轻松管理多个Promise,使其并行执行,但是如果需要控制并发数量时,我们就需要实现一个控制函数。
const limitPromises = (promises, limit) => {
return new Promise((resolve, reject) => {
let result = [];
for (let j = 0; j < promises.length; j++) {
Promise.resolve(promises[j]).then(value => {
if (result.length >= Math.min(promises.length, limit)) {
resolve(result);
} else {
result.push(value);
}
}).catch(reject);
}
});
}
超时控制
对Promise进行超时控制,指定一定时间无法返回的直接reject
const timeoutPromise = (promise, time) => {
return Promise.race([promise, new Promise((r,j) => {
setTimeout(() => {j(new Error('Timeout'))}, time);
})])
}
取消控制
原生Promise是无法取消的,但是我们可以重新封装一个
class CancellablePromise {
constructor(executor) {
this.isCancelled = false;
this.resolve = null;
this.reject = null;
const innerPromise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
executor((...args) => {
if (!this.isCancelled) {
resolve(...args);
}
}, (...args) => {
if (!this.isCancelled) {
reject(...args);
}
});
});
Object.defineProperty(this, 'then', {
value: innerPromise.then.bind(innerPromise)
});
Object.defineProperty(this, 'catch', {
value: innerPromise.catch.bind(innerPromise)
});
}
cancel() {
this.isCancelled = true;
if (this.resolve) {
this.reject(new Error('Promise cancelled'));
this.resolve = null;
this.reject = null;
}
}
}
// 使用示例
const task = new CancellablePromise((resolve, reject) => {
setTimeout(() => {
resolve('Task completed');
}, 5000);
});
task.then(result => {
console.log(result);
}).catch(error => {
console.error(error.message);
});
// 取消任务
setTimeout(() => {
task.cancel();
}, 1000);
查询状态
原生Promise是无法查询状态的
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.promise = new Promise((resolve, reject) => {
executor((...args) => {
this.state = 'fulfilled';
this.value = args.length === 1 ? args[0] : args;
resolve(this.value);
}, (...args) => {
this.state = 'rejected';
this.reason = args.length === 1 ? args[0] : args;
reject(this.reason);
});
});
}
then(onFulfilled, onRejected) {
return this.promise.then(onFulfilled, onRejected);
}
catch(onRejected) {
return this.promise.catch(onRejected);
}
getState() {
return this.state;
}
getValue() {
if (this.state === 'fulfilled') {
return this.value;
}
throw new Error('Promise is not fulfilled');
}
getReason() {
if (this.state === 'rejected') {
return this.reason;
}
throw new Error('Promise is not rejected');
}
}
// 使用示例
const myPromise = new MyPromise((resolve, reject) => {
setTimeout(() => reject('Success!'), 1000);
});
console.log(myPromise.getState()); // 'pending'
myPromise.then(result => {
console.log(result); // 'Success!'
console.log(myPromise.getState()); // 'fulfilled'
});
顺序控制
使Promise按顺序执行
const seqPromises = promises => {
return promises.reduce((pre, next) => pre.then(next), Promise.resolve());
}
const asyncOperations = [
() => new Promise((resolve) => setTimeout(() => resolve('Result 1'), 1000)),
(result1) => new Promise((resolve) => setTimeout(() => resolve(`Result 2 based on ${result1}`), 1000)),
(result2) => new Promise((resolve) => setTimeout(() => resolve(`Result 3 based on ${result2}`), 1000))
];
seqPromises(asyncOperations).then(res => console.log(res));
重试机制
在JavaScript中,处理Promise重试通常涉及创建一个能够自动重试失败操作的机制。这可以通过封装一个自定义的retry函数来实现,该函数接受一个返回Promise的函数作为参数,并根据指定的重试次数和延迟时间来决定是否重新执行该Promise。
以下是一个简单的retry函数实现,它接受三个参数:一个返回Promise的异步函数fn,最大重试次数maxRetries,以及每次重试之间的延迟时间delay(以毫秒为单位)。
function retry(fn, maxRetries = 3, delay = 1000) {
return new Promise((resolve, reject) => {
let attempt = 0;
const attemptFn = () => {
if (attempt >= maxRetries) {
reject(new Error(`Max retries reached: ${maxRetries}`));
return;
}
attempt++;
fn()
.then(resolve)
.catch((error) => {
setTimeout(() => {
console.warn(`Attempt ${attempt} failed, retrying...`, error);
attemptFn();
}, delay);
});
};
attemptFn();
});
}
// 使用示例
const asyncOperation = () => {
return new Promise((resolve, reject) => {
// 模拟一个可能会失败的异步操作
const success = Math.random() > 0.5; // 50% 的成功率
if (success) {
resolve('Operation succeeded');
} else {
reject(new Error('Operation failed'));
}
});
};
retry(asyncOperation, 5, 2000)
.then((result) => {
console.log('Final result:', result);
})
.catch((error) => {
console.error('Operation failed after retries:', error);
});
确保Promise只解决一次
在JavaScript中,Promise 对象的设计初衷就是确保它们只解决(resolve)一次或拒绝(reject)一次。一旦 Promise 进入已解决(fulfilled)或已拒绝(rejected)状态,它的状态就是最终的,不可再改变。这意味着你不能“重新解决”或“重新拒绝”一个已经解决或拒绝的 Promise。
然而,有时候你可能会遇到需要确保某个异步操作只被触发一次的情况,即使这个操作被封装在一个可能多次调用的函数或回调中。这通常不是 Promise 本身的问题,而是你如何使用和管理 Promise 的问题。
function once(fn) {
let resolved = false;
let result;
return function(...args) {
if (!resolved) {
resolved = true;
result = fn.apply(this, args);
if (result && typeof result.then === 'function') {
// 如果 fn 返回一个 Promise,则确保它只被处理一次
result.finally(() => {
// 可以在这里做一些清理工作,但通常不需要
});
}
}
return result;
};
}
// 使用示例
const asyncOperation = () => new Promise(resolve => setTimeout(resolve, 1000, 'done'));
const asyncOnce = once(asyncOperation);
asyncOnce().then(console.log); // 输出 'done'
asyncOnce().then(console.log); // 不会执行,因为 Promise 已经被解决
Promise.allSettled
使用 Promise.allSettled() 的一个常见场景是当你需要并行执行多个异步操作,并且不关心其中任何一个操作是否失败,只想在所有操作完成后获取它们的结果(无论是成功还是失败)。
const promise1 = Promise.resolve(42);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'error'));
const promise3 = Promise.resolve('Hello World');
Promise.allSettled([promise1, promise2, promise3])
.then((results) => {
results.forEach((result) => {
if (result.status === 'fulfilled') {
console.log(`Fulfilled with value: ${result.value}`);
} else if (result.status === 'rejected') {
console.log(`Rejected with reason: ${result.reason}`);
}
});
});
以上是对于Promise一些特殊用法的总结,你学废了吗?
猜你喜欢
- 2025-01-03 ant design Modal关闭时清除数据的解决方案
- 2025-01-03 Flink状态管理详解:Keyed State和Operator List State深度解析
- 2025-01-03 电气自动化专业词汇中英文对照表
- 2025-01-03 事务相关知识集锦
- 2025-01-03 FlexSim常用脚本语言汇总--搜集最常用
- 2025-01-03 停止javascript的ajax请求,取消axios请求,取消reactfetch请求
- 2025-01-03 MySQL进阶垫脚石:线程长时间处于killed状态怎么破?
- 2025-01-03 ReentrantLock的底层原理
- 2025-01-03 还搞不定 SQL 阻塞与超时?早晚得出事!
- 2025-01-03 细说ReactiveCocoa的冷信号与热信号(二):为什么要区分冷热信号
- 最近发表
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 计算机网络的拓扑结构是指() (45)
- 稳压管的稳压区是工作在什么区 (45)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)