LeetCode 几道 Promises 和 Time 的题目
目的:对于 Promises 和 Time 有个基本的使用了解,核心代码用于生产
文章格式:
- 带编号的题目
- 简单的题目描述(官网的题目描述有时读不懂,完整版请移步官网)
- 代码片段(可以通过所有用例)
2723. 两个 Promise 对象相加
给你两个 Promise 对象(比如 promise1 和 promise2),每个 Promise 最终都会解析成一个数字。
你需要写一个函数,同时等待这两个 Promise 完成,然后把它们解析出来的两个数字相加,最后返回一个新的 Promise,这个新 Promise 会解析为相加的结果。
js
/**
* @param {Promise} promise1
* @param {Promise} promise2
* @return {Promise}
*/
var addTwoPromises = async function (promise1, promise2) {
const [val1, val2] = await Promise.all([promise1, promise2]);
return val1 + val2;
}; // 优雅
/**
* addTwoPromises(Promise.resolve(2), Promise.resolve(2))
* .then(console.log); // 4
*/
2621. 睡眠函数
写一个"睡眠函数",它接受一个毫秒数作为参数,让程序"睡"一会儿(等待指定的时间),然后什么都不返回(或者返回一个 Promise,在等待完成后解析)
js
/**
* @param {number} millis
* @return {Promise}
*/
async function sleep(millis) {
return new Promise((resolve) => setTimeout(resolve, millis));
}
/**
* let t = Date.now()
* sleep(100).then(() => console.log(Date.now() - t)) // 100
*/
2715. 执行可取消的延迟函数
你要创建一个"双计时器"系统:
主要功能:
- 主任务延迟 :函数
fn
在t
毫秒后执行 - 自动取消机制 :在
cancelTimeMs
毫秒后会自动调用取消函数 - 手动取消:可以提前手动取消主任务
js
/**
* @param {Function} fn
* @param {Array} args
* @param {number} t
* @return {Function}
*/
function cancellable(fn, args, t) {
// 用 setTimeout 安排 fn 执行
const timerId = setTimeout(() => {
fn(...args);
}, t);
// 返回的 cancelFn 函数
// 当它被调用时,清除定时器,阻止 fn 执行
return function cancelFn() {
clearTimeout(timerId);
};
}
/**
* const result = [];
*
* const fn = (x) => x * 5;
* const args = [2], t = 20, cancelTimeMs = 50;
*
* const start = performance.now();
*
* const log = (...argsArr) => {
* const diff = Math.floor(performance.now() - start);
* result.push({"time": diff, "returned": fn(...argsArr)});
* }
*
* const cancel = cancellable(log, args, t);
*
* const maxT = Math.max(t, cancelTimeMs);
*
* setTimeout(cancel, cancelTimeMs);
*
* setTimeout(() => {
* console.log(result); // [{"time":20,"returned":10}]
* }, maxT + 15)
*/
2725. 间隔取消
你要创建一个"定时重复执行 + 自动停止"的系统:
核心功能:
- 立即执行 :函数
fn
立即用args
参数执行一次 - 间隔重复 :之后每隔
t
毫秒重复执行一次 - 自动停止 :在
cancelTimeMs
毫秒后自动停止所有执行 - 返回控制:返回一个取消函数(虽然会自动取消)
js
/**
* @param {Function} fn
* @param {Array} args
* @param {number} t
* @return {Function}
*/
var cancellable = function (fn, args, t) {
fn(...args);
let timer = setInterval(fn, t, ...args);
return () => clearInterval(timer);
};
/**
* const result = [];
*
* const fn = (x) => x * 2;
* const args = [4], t = 35, cancelTimeMs = 190;
*
* const start = performance.now();
*
* const log = (...argsArr) => {
* const diff = Math.floor(performance.now() - start);
* result.push({"time": diff, "returned": fn(...argsArr)});
* }
*
* const cancel = cancellable(log, args, t);
*
* setTimeout(cancel, cancelTimeMs);
*
* setTimeout(() => {
* console.log(result); // [
* // {"time":0,"returned":8},
* // {"time":35,"returned":8},
* // {"time":70,"returned":8},
* // {"time":105,"returned":8},
* // {"time":140,"returned":8},
* // {"time":175,"returned":8}
* // ]
* }, cancelTimeMs + t + 15)
*/
2637. 有时间限制的 Promise 对象
你要创建一个"带超时控制的异步函数包装器":
核心功能:
- 正常执行 :让原异步函数
fn
正常执行 - 时间限制 :如果
fn
在t
毫秒内完成,返回结果 - 超时处理 :如果
fn
执行超过t
毫秒,抛出错误 "Time Limit Exceeded" - 参数传递:正确传递所有参数给原函数
js
/**
* @param {Function} fn
* @param {number} t
* @return {Function}
*/
function timeLimit(fn, t) {
return async function (...args) {
// 创建一个 Promise,用于表示"超时"
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject("Time Limit Exceeded");
}, t);
});
// 同时运行 fn 和超时计时器
// 谁先完成,就用谁的结果
return Promise.race([
fn(...args), // 执行原始异步函数
timeoutPromise, // 超时后触发 reject
]);
};
}
/**
* const limited = timeLimit((t) => new Promise(res => setTimeout(res, t)), 100);
* limited(150).catch(console.log) // "Time Limit Exceeded" at t=100ms
*/
2622. 有时间限制的缓存
请你设计一个有时间限制的缓存(Time Limited Cache)类,该类应该支持以下操作:
方法定义:
-
set(key, value, duration)
- 接受一个整数键
key
,一个整数值value
和一个以毫秒为单位的持续时间duration
- 在指定的持续时间
duration
后,该键值对自动失效 - 如果该键已经存在且未过期,则更新其值和持续时间
- 如果相同的未过期键已经存在,该方法将返回
true
,否则返回false
- 接受一个整数键
-
get(key)
- 如果键
key
存在且未过期,返回对应的值 - 如果键不存在或已过期,返回
-1
- 如果键
-
count()
(可选,部分变体)- 返回当前未过期的键值对数量
js
var TimeLimitedCache = function () {
this.cache = new Map(); // 使用Map存储键值对和过期时间
};
/**
* @param {number} key
* @param {number} value
* @param {number} duration time until expiration in ms
* @return {boolean} if un-expired key already existed
*/
TimeLimitedCache.prototype.set = function (key, value, duration) {
const currentTime = Date.now();
const expirationTime = currentTime + duration; // 存储过期时间戳
// 检查键是否已存在且未过期
const existing = this.cache.get(key);
const isExisting = existing && existing.expirationTime > currentTime;
// 设置或更新值
this.cache.set(key, {
value: value,
expirationTime: expirationTime,
});
return !!isExisting; // 返回是否存在未过期的键
};
/**
* @param {number} key
* @return {number} value associated with key
*/
TimeLimitedCache.prototype.get = function (key) {
const currentTime = Date.now();
const item = this.cache.get(key);
// 如果键不存在或已过期,返回-1
if (!item || item.expirationTime <= currentTime) {
return -1;
}
return item.value; // 返回未过期的值
};
/**
* @return {number} count of non-expired keys
*/
TimeLimitedCache.prototype.count = function () {
const currentTime = Date.now();
let count = 0;
// 遍历所有键,统计未过期的数量
for (const [key, item] of this.cache) {
if (item.expirationTime > currentTime) {
count++;
}
}
return count;
};
2627. 函数防抖
你要创建一个"防抖函数",就像电梯关门按钮:
核心功能:
- 延迟执行 :函数调用后不会立即执行,而是延迟
t
毫秒 - 重置计时:如果在延迟期间再次调用,重新开始计时
- 最终执行 :只有在最后一次调用后的
t
毫秒内没有新调用,才会真正执行
js
/**
* @param {Function} fn
* @param {number} t milliseconds
* @return {Function}
*/
var debounce = function (fn, t) {
let timeoutId;
return function (...args) {
// 清除之前的定时器(如果存在)
if (timeoutId) {
clearTimeout(timeoutId);
}
// 设置新的定时器,延迟 t 毫秒后执行 fn
timeoutId = setTimeout(() => {
fn.apply(this, args); // 保持 this 上下文和参数
}, t);
};
};
2721. 并行执行异步函数
你要自己实现一个 Promise.all
的功能:
核心功能:
- 并行执行:同时执行所有异步函数(不按顺序等待)
- 全部成功:所有 Promise 都成功时,返回结果数组(保持原始顺序)
- 快速失败:任何一个 Promise 失败,立即拒绝(返回第一个错误)
- 不用 Promise.all:需要自己实现这个功能
js
/**
* @param {Array<Function>} functions
* @return {Promise<any>}
*/
var promiseAll = function (functions) {
return new Promise((resolve, reject) => {
const n = functions.length;
const results = new Array(n);
let completedCount = 0;
// 如果数组为空,立即resolve空数组
if (n === 0) return resolve(results);
functions.forEach((fn, index) => {
// 执行每个函数获取Promise
fn()
.then((result) => {
// 将结果放在正确的位置
results[index] = result;
completedCount++;
// 当所有Promise都完成时,resolve结果数组
if (completedCount === n) {
resolve(results);
}
})
.catch((error) => {
// 任何一个Promise失败,立即reject
reject(error);
});
});
});
};
/**
* const promise = promiseAll([() => new Promise(res => res(42))])
* promise.then(console.log); // [42]
*/