微信搜索“好朋友乐平”关注公众号。
请你编写一个异步函数 promisePool
,它接收一个异步函数数组 functions
和 池限制 n
。它应该返回一个 promise
对象,当所有输入函数都执行完毕后,promise
对象就执行完毕。
池限制 定义是一次可以挂起的最多 promise
对象的数量。promisePool
应该开始执行尽可能多的函数,并在旧的 promise
执行完毕后继续执行新函数。promisePool
应该先执行 functions[i]
,再执行 functions[i + 1]
,然后执行 functions[i + 2]
,等等。当最后一个 promise
执行完毕时,promisePool
也应该执行完毕。
例如,如果 n = 1
, promisePool
在序列中每次执行一个函数。然而,如果 n = 2
,它首先执行两个函数。当两个函数中的任何一个执行完毕后,再执行第三个函数(如果它是可用的),依此类推,直到没有函数要执行为止。
你可以假设所有的 functions
都不会被拒绝。对于 promisePool
来说,返回一个可以解析任何值的 promise
都是可以接受的。
示例 1:
输入:
functions = [
() => new Promise(res => setTimeout(res, 300)),
() => new Promise(res => setTimeout(res, 400)),
() => new Promise(res => setTimeout(res, 200))
]
n = 2
输出:[[300,400,500],500]
解释
传递了三个函数。它们的睡眠时间分别为 300ms、 400ms 和 200ms。
在 t=0 时,执行前两个函数。池大小限制达到 2。
当 t=300 时,第一个函数执行完毕后,执行第3个函数。池大小为 2。
在 t=400 时,第二个函数执行完毕后。没有什么可执行的了。池大小为 1。
在 t=500 时,第三个函数执行完毕后。池大小为 0,因此返回的 promise 也执行完成。
示例 2:
输入:
functions = [
() => new Promise(res => setTimeout(res, 300)),
() => new Promise(res => setTimeout(res, 400)),
() => new Promise(res => setTimeout(res, 200))
]
n = 5
输出:[[300,400,200],400]
解释:
在 t=0 时,所有3个函数都被执行。池的限制大小 5 永远不会满足。
在 t=200 时,第三个函数执行完毕后。池大小为 2。
在 t=300 时,第一个函数执行完毕后。池大小为 1。
在 t=400 时,第二个函数执行完毕后。池大小为 0,因此返回的 promise 也执行完成。
示例 3:
输入:
functions = [
() => new Promise(res => setTimeout(res, 300)),
() => new Promise(res => setTimeout(res, 400)),
() => new Promise(res => setTimeout(res, 200))
]
n = 1
输出:[[300,700,900],900]
解释:
在 t=0 时,执行第一个函数。池大小为1。
当 t=300 时,第一个函数执行完毕后,执行第二个函数。池大小为 1。
当 t=700 时,第二个函数执行完毕后,执行第三个函数。池大小为 1。
在 t=900 时,第三个函数执行完毕后。池大小为 0,因此返回的 Promise 也执行完成。
实现
type F = () => Promise<any>;
function promisePool(functions: F[], n: number): Promise<any[]> {
let fNext = 0; // 下一个要执行的函数的索引
// 递归调用该函数以依次执行下一个函数
const evaluateNext = async (): Promise<void> => {
if (fNext >= functions.length) {
// 如果所有函数都已执行,则退出
return;
}
const fn = functions[fNext++]; // 获取下一个要执行的函数
await fn(); // 执行函数并等待其完成
await evaluateNext(); // 递归调用 evaluateNext,继续执行下一个函数
};
// 同时启动 n 个 evaluateNext()调用来保持 n 个异步任务并发
const runners = new Array(n).fill(null).map(() => evaluateNext());
// 等待所有启动的任务完成
return Promise.all(runners)
}
给定两个 promise 对象 promise1 和 promise2,返回一个新的 promise。promise1 和 promise2 都会被解析为一个数字。返回的 Promise 应该解析为这两个数字的和。
示例 1:
输入:
promise1 = new Promise(resolve => setTimeout(() => resolve(2), 20)),
promise2 = new Promise(resolve => setTimeout(() => resolve(5), 60))
输出:7
解释:两个输入的 Promise 分别解析为值 2 和 5。返回的 Promise 应该解析为 2 + 5 = 7。返回的 Promise 解析的时间不作为判断条件。
示例 2:
输入:
promise1 = new Promise(resolve => setTimeout(() => resolve(10), 50)),
promise2 = new Promise(resolve => setTimeout(() => resolve(-12), 30))
输出:-2
解释:两个输入的 Promise 分别解析为值 10 和 -12。返回的 Promise 应该解析为 10 + -12 = -2。
type P = Promise<number>
async function addTwoPromises(promise1: P, promise2: P): P {
};
/**
* addTwoPromises(Promise.resolve(2), Promise.resolve(2))
* .then(console.log); // 4
*/
实现
async function addTwoPromises(promise1: Promise<number>, promise2: Promise<number>): Promise<number> {
return await promise1 + await promise2
};
async function addTwoPromises(promise1: Promise<number>, promise2: Promise<number>): Promise<number> {
return await Promise.all([promise1, promise2]).then(([a, b]) => a + b)
};
async function addTwoPromises(promise1: Promise<number>, promise2: Promise<number>): Promise<number> {
const [a, b] = await Promise.all([promise1, promise2])
return a + b
};
async function addTwoPromises(promise1: Promise<number>, promise2: Promise<number>): Promise<number> {
return new Promise((resolve, reject) => {
Promise.all([promise1, promise2]).then(([a, b]) => {
resolve(a + b)
}).catch(reject)
})
};
请你编写一个函数,它接受一个异步函数 fn 和一个以毫秒为单位的时间 t。它应根据限时函数返回一个有 限时 效果的函数。函数 fn 接受提供给 限时 函数的参数。
限时 函数应遵循以下规则:
如果 fn 在 t 毫秒的时间限制内完成,限时 函数应返回结果。
如果 fn 的执行超过时间限制,限时 函数应拒绝并返回字符串 “Time Limit Exceeded” 。
示例 1:
输入:
fn = async (n) => {
await new Promise(res => setTimeout(res, 100));
return n * n;
}
inputs = [5]
t = 50
输出:{"rejected":"Time Limit Exceeded","time":50}
解释:
const limited = timeLimit(fn, t)
const start = performance.now()
let result;
try {
const res = await limited(...inputs)
result = {"resolved": res, "time": Math.floor(performance.now() - start)};
} catch (err) {
result = {"rejected": err, "time": Math.floor(performance.now() - start)};
}
console.log(result) // 输出结果
提供的函数设置在 100ms 后执行完成,但是设置的超时时间为 50ms,所以在 t=50ms 时拒绝因为达到了超时时间。
示例 2:
输入:
fn = async (n) => {
await new Promise(res => setTimeout(res, 100));
return n * n;
}
inputs = [5]
t = 150
输出:{"resolved":25,"time":100}
解释:
在 t=100ms 时执行 5*5=25 ,没有达到超时时间。
示例 3:
输入:
fn = async (a, b) => {
await new Promise(res => setTimeout(res, 120));
return a + b;
}
inputs = [5,10]
t = 150
输出:{"resolved":15,"time":120}
解释:
在 t=120ms 时执行 5+10=15,没有达到超时时间。
示例 4:
输入:
fn = async () => {
throw "Error";
}
inputs = []
t = 1000
输出:{"rejected":"Error","time":0}
解释:
此函数始终丢出 Error
提示:
0 <= inputs.length <= 10
0 <= t <= 1000
fn 返回一个 Promise 对象
实现
type Fn = (...params: any[]) => Promise<any>;
function timeLimit(fn: Fn, t: number): Fn {
return async function(...args) {
return new Promise(async (resolve, reject) => {
const timeout = setTimeout(() => {
reject("Time Limit Exceeded");
}, t);
try {
const result = await fn(...args);
resolve(result);
} catch(err) {
reject(err);
}
clearTimeout(timeout);
});
};
};
/**
* const limited = timeLimit((t) => new Promise(res => setTimeout(res, t)), 100);
* limited(150).catch(console.log) // "Time Limit Exceeded" at t=100ms
*/
给定一个函数数组 functions 和一个数字 ms,返回一个新的函数数组。
functions 是一个返回 Promise 对象的函数数组。
ms 表示延迟的时间,以毫秒为单位。它决定了在新数组中的每个函数返回的 Promise 在解析之前等待的时间。
新数组中的每个函数应该返回一个 Promise 对象,在延迟了 ms 毫秒后解析,保持原始 functions 数组中的顺序。delayAll 函数应确保从 functions 中的每个 Promise 都被延迟执行,形成返回延迟的 Promise 的函数的新数组。
示例 1:
输入:
functions = [
() => new Promise((resolve) => setTimeout(resolve, 30))
],
ms = 50
输出:[80]
解释:数组中的 Promise 在 30 毫秒后解析,但被延迟了 50 毫秒,所以总共延迟了 30 毫秒 + 50 毫秒 = 80 毫秒。
示例 2:
输入:
functions = [
() => new Promise((resolve) => setTimeout(resolve, 50)),
() => new Promise((resolve) => setTimeout(resolve, 80))
],
ms = 70
输出:[120,150]
解释:数组中的 Promise 在 50 毫秒和 80 毫秒后解析,但它们被延迟了 70 毫秒,所以总共延迟了 50 毫秒 + 70 毫秒 = 120 毫秒 和 80 毫秒 + 70 毫秒 = 150 毫秒。
提示:
functions 是一个返回 Promise 对象的函数数组
10 <= ms <= 500
1 <= functions.length <= 10
实现
type Fn = () => Promise<any>
function delayAll(functions: Fn[], ms: number): Fn[] {
return functions.map(fn => () => new Promise(res => {
setTimeout(() => {
res(fn())
}, ms)
}))
};
编写一个函数,接受另一个函数 fn ,并将基于回调函数的函数转换为基于 Promise 的函数。
promisify 函数接受一个函数 fn ,fn 将回调函数作为其第一个参数,并且还可以接受其他额外的参数。
promisfy 返回一个新函数,新函数会返回一个 Promise 对象。当回调函数被成功调用时,新函数返回的 Promise 对象应该使用原始函数的结果进行解析;当回调函数被调用出现错误时,返回的 Promise 对象应该被拒绝并携带错误信息。最终返回的基于 Promise 的函数应该接受额外的参数作为输入。
以下是一个可以传递给 promisify 的函数示例:
function sum(callback, a, b) {
if (a < 0 || b < 0) {
const err = Error('a and b must be positive');
callback(undefined, err);
} else {
callback(a + b);
}
}
这是基于 Promise 的等效代码:
async function sum(a, b) {
if (a < 0 || b < 0) {
throw Error('a and b must be positive');
} else {
return a + b;
}
}
示例 1:
输入:
fn = (callback, a, b, c) => {
return callback(a * b * c);
}
args = [1, 2, 3]
输出:{"resolved": 6}
解释:
const asyncFunc = promisify(fn);
asyncFunc(1, 2, 3).then(console.log); // 6
fn 以回调函数作为第一个参数和 args 作为其余参数进行调用。当使用 (1, 2, 3) 调用时,基于 Promise 的 fn 将解析为值 6。
示例 2:
输入:
fn = (callback, a, b, c) => {
callback(a * b * c, "Promise Rejected");
}
args = [4, 5, 6]
输出:{"rejected": "Promise Rejected"}
解释:
const asyncFunc = promisify(fn);
asyncFunc(4, 5, 6).catch(console.log); // "Promise Rejected"
fn 以回调函数作为第一个参数和 args 作为其余参数进行调用。在回调函数的第二个参数中,接受一个错误消息,因此当调用 fn 时,Promise 被拒绝并携带回调函数中提供的错误消息。请注意,不管将什么作为回调函数的第一个参数传递都无关紧要。
提示:
1 <= args.length <= 100
0 <= args[i] <= 104
实现
type CallbackFn = (
next: (data: number, error: string) => void,
...args: number[]
) => void
type Promisified = (...args: number[]) => Promise<number>
function promisify(fn: CallbackFn): Promisified {
return async function(...args) {
return new Promise((resolve, reject) => {
fn((data: number, error: string) => {
if (error) reject(error);
resolve(data);
}, ...args);
});
};
};
给定一个数组 functions,返回一个 promise 对象 promise。functions 是一个返回多个 promise 对象 fnPromise 的函数数组。每个 fnPromise 可以被解析(resolved)或拒绝(rejected)。
如果 fnPromise 被解析:
obj = { status: "fulfilled", value: resolved value}
如果 fnPromise 被拒绝:
obj = { status: "rejected", reason: 拒绝的原因(捕获的错误消息)}
该 promise 应该返回一个包含这些对象 obj 的数组。数组中的每个 obj 应该对应原始函数数组中的多个 promise 对象,并保持相同的顺序。
请在不使用内置方法 Promise.allSettled() 的情况下实现它。
示例 1:
输入:functions = [
() => new Promise(resolve => setTimeout(() => resolve(15), 100))
]
输出:{"t":100,"values":[{"status":"fulfilled","value":15}]}
解释:
const time = performance.now()
const promise = promiseAllSettled(functions);
promise.then(res => {
const out = {t: Math.floor(performance.now() - time), values: res}
console.log(out) // {"t":100,"values":[{"status":"fulfilled","value":15}]}
})
返回的 promise 在 100 毫秒内解析。由于函数数组中的 promise 被解析,返回的 promise 的解析值设置为[{"status":"fulfilled","value":15}]。
示例 2:
输入:functions = [
() => new Promise(resolve => setTimeout(() => resolve(20), 100)),
() => new Promise(resolve => setTimeout(() => resolve(15), 100))
]
输出:
{
"t":100,
"values": [
{"status":"fulfilled","value":20},
{"status":"fulfilled","value":15}
]
}
解释:返回的 promise 在 100 毫秒内解析,因为解析时间取决于需要最长时间来解析的 promise。由于函数数组中的 promises 被解析,返回的 promise 的解析值设置为[{"status":"fulfilled","value":20},{"status":"fulfilled","value":15}]。
示例 3:
输入:functions = [
() => new Promise(resolve => setTimeout(() => resolve(30), 200)),
() => new Promise((resolve, reject) => setTimeout(() => reject("Error"), 100))
]
输出:
{
"t":200,
"values": [
{"status":"fulfilled","value":30},
{"status":"rejected","reason":"Error"}
]
}
解释:返回的 promise 在 200 毫秒内解析,因为解析时间取决于需要最长时间来解析的 promise。由于函数数组中的一个 promise 被解析,另一个被拒绝,返回的 promise 的解析值设置为[{"status":"fulfilled","value":30},{"status":"rejected","reason":"Error"}]。数组中的每个对象对应原始函数数组中的 promise,并保持相同的顺序。
提示:
1 <= functions.length <= 10
实现
type FulfilledObj = {
status: 'fulfilled';
value: string;
}
type RejectedObj = {
status: 'rejected';
reason: string;
}
type Obj = FulfilledObj | RejectedObj;
function promiseAllSettled(functions: Function[]): Promise<Obj[]> {
return new Promise((resolve) => {
const resultArray = [];
let completedCount = 0;
for (let i = 0; i < functions.length; i++) {
const fnPromise = functions[i]();
fnPromise
.then((value) => {
resultArray[i] = { status: "fulfilled", value };
})
.catch((reason) => {
resultArray[i] = { status: "rejected", reason };
})
.finally(() => {
completedCount++;
if (completedCount === functions.length) {
resolve(resultArray);
}
});
}
});
};
/**
* const functions = [
* () => new Promise(resolve => setTimeout(() => resolve(15), 100))
* ]
* const time = performance.now()
*
* const promise = promiseAllSettled(functions);
*
* promise.then(res => {
* const out = {t: Math.floor(performance.now() - time), values: res}
* console.log(out) // {"t":100,"values":[{"status":"fulfilled","value":15}]}
* })
*/
给定一个异步函数数组 functions,返回一个新的 promise 对象 promise。数组中的每个函数都不接受参数并返回一个 promise。所有的 promise 都应该并行执行。
promise resolve 条件:
当所有从 functions 返回的 promise 都成功的并行解析时。promise 的解析值应该是一个按照它们在 functions 中的顺序排列的 promise 的解析值数组。promise 应该在数组中的所有异步函数并行执行完成时解析。
promise reject 条件:
当任何从 functions 返回的 promise 被拒绝时。promise 也会被拒绝,并返回第一个拒绝的原因。
请在不使用内置的 Promise.all 函数的情况下解决。
示例 1:
输入:functions = [
() => new Promise(resolve => setTimeout(() => resolve(5), 200))
]
输出:{"t": 200, "resolved": [5]}
解释:
promiseAll(functions).then(console.log); // [5]
单个函数在 200 毫秒后以值 5 成功解析。
示例 2:
输入:functions = [
() => new Promise(resolve => setTimeout(() => resolve(1), 200)),
() => new Promise((resolve, reject) => setTimeout(() => reject("Error"), 100))
]
输出:{"t": 100, "rejected": "Error"}
解释:由于其中一个 promise 被拒绝,返回的 promise 也在同一时间被拒绝并返回相同的错误。
示例 3:
输入:functions = [
() => new Promise(resolve => setTimeout(() => resolve(4), 50)),
() => new Promise(resolve => setTimeout(() => resolve(10), 150)),
() => new Promise(resolve => setTimeout(() => resolve(16), 100))
]
输出:{"t": 150, "resolved": [4, 10, 16]}
解释:所有的 promise 都成功执行。当最后一个 promise 被解析时,返回的 promise 也被解析了。
提示:
函数 functions 是一个返回 promise 的函数数组
1 <= functions.length <= 10
实现
type Fn<T> = () => Promise<T>
async function promiseAll<T>(functions: (() => Promise<T>)[]): Promise<T[]> {
return new Promise<T[]>((resolve, reject) => {
if(functions.length === 0) {
resolve([]);
return;
}
const res: T[] = new Array(functions.length).fill(null);
let resolvedCount = 0;
functions.forEach(async (el, idx) => {
try {
const subResult = await el();
res[idx] = subResult;
resolvedCount++;
if(resolvedCount === functions.length) {
resolve(res);
}
} catch(err) {
reject(err);
}
});
});
};
/**
* const promise = promiseAll([() => new Promise(res => res(42))])
* promise.then(console.log); // [42]
*/
请你编写一个异步函数,它接收一个正整数参数 millis ,并休眠 millis 毫秒。要求此函数可以解析任何值。
示例 1:
输入:millis = 100
输出:100
解释:
在 100ms 后此异步函数执行完时返回一个 Promise 对象
let t = Date.now();
sleep(100).then(() => {
console.log(Date.now() - t); // 100
});
示例 2:
输入:millis = 200
输出:200
解释:在 200ms 后函数执行完时返回一个 Promise 对象
提示:
1 <= millis <= 1000
实现
async function sleep(millis: number): Promise<void> {
return new Promise<void>(resolve => {
setTimeout(resolve, millis);
});
}
描述:实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有 limit 个。
实现
type PromiseCreator = () => Promise<void>;
class Scheduler {
private queue: PromiseCreator[]; // 用队列保存正在执行的任务
private runCount: number; // 计数正在执行的任务个数
private maxCount: number; // 允许并发的最大个数
constructor(limit: number) {
this.queue = [];
this.runCount = 0;
this.maxCount = limit;
}
add(time: number, data: string) {
const promiseCreator: PromiseCreator = () => {
return new Promise<void>((resolve) => {
setTimeout(() => {
console.log(data);
resolve();
}, time);
});
}
this.queue.push(promiseCreator);
// 每次添加的时候都会尝试去执行任务
this.request();
}
private request() {
// 队列中还有任务才会被执行
if (this.queue.length && this.runCount < this.maxCount) {
this.runCount++;
// 执行先加入队列的函数
this.queue.shift()!().then(() => {
this.runCount--;
// 尝试进行下一次任务
this.request();
});
}
}
}
// 测试
const scheduler = new Scheduler(2);
const addTask = (time: number, data: string) => {
scheduler.add(time, data);
}
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
// 输出结果 2 3 1 4
实现
type CancellablePromise<T> = [Promise<T>, () => void];
function makeCancellable<T>(promise: Promise<T>): CancellablePromise<T> {
let rejectFn: (reason?: any) => void;
const wrappedPromise = new Promise<T>((resolve, reject) => {
rejectFn = reject; // 保存 reject 函数引用以便后续调用
promise.then(
(value) => {
if (rejectFn !== null) { // 如果没有被取消,那么解决 wrappedPromise
resolve(value);
rejectFn = null; // 清除 rejectFn 引用,避免内存泄漏
}
},
(error) => {
if (rejectFn !== null) { // 如果没有被取消,那么拒绝 wrappedPromise
reject(error);
rejectFn = null; // 清除 rejectFn 引用,避免内存泄漏
}
}
);
});
const cancel = () => {
if (rejectFn !== null) {
rejectFn({ cancelled: true }); // 立即拒绝 wrappedPromise
rejectFn = null; // 防止内存泄漏,清除 rejectFn 引用
}
};
return [wrappedPromise, cancel];
}
// 使用示例
const [cancellablePromise, cancel] = makeCancellable(new Promise<string>((resolve) => {
setTimeout(() => {
resolve("Resolved after 2 seconds");
}, 2000);
}));
cancellablePromise
.then((result) => {
console.log(result);
})
.catch((error) => {
if (error && error.cancelled) {
console.log("Promise was cancelled");
} else {
console.log("Promise was rejected with error:", error);
}
});
// 立即取消 Promise
cancel();
// Node.js 风格的 myFunction
function myFunction(cb1, cb2, cb3) {
// 模拟异步操作,例如 I/O
setTimeout(() => cb1(null, "result1"), Math.floor(Math.random() * 1000));
setTimeout(() => cb2(null, "result2"), Math.floor(Math.random() * 1000));
setTimeout(() => cb3(null, "result3"), Math.floor(Math.random() * 1000));
}
// 实现 myFunctionPromise,将 myFunction Promise 化。
// cb callback 无 error 时,则为 resolve
// 测试用例
test("a", async () => {
try {
const results = await myFunctionPromise();
console.log(results); // 输出: ['result1', 'result2', 'result3']
// 这里我们期望返回的 Promise 被成功地 resolve,并且结果按次序排列
expect(results).toEqual(["result1", "result2", "result3"]);
} catch (err) {
// 这里处理可能出现的错误
console.error(err);
}
});
实现
// Promise 包装器,保证次序
function myFunctionPromise() {
return new Promise((resolve, reject) => {
let results = new Array(3); // 创建一个长度为 3 的数组来存储结果
let count = 0;
let hasErrorOccurred = false;
function createFinalCallback(index) {
return function (err, result) {
if (hasErrorOccurred) return;
if (err) {
hasErrorOccurred = true;
return reject(err);
}
results[index] = result; // 根据回调的标识符存储结果
count++;
if (count === 3) {
resolve(results); // 当所有回调都执行完毕时,按顺序解决 Promise
}
};
}
myFunction(
createFinalCallback(0),
createFinalCallback(1),
createFinalCallback(2)
);
});
}
微信搜索“好朋友乐平”关注公众号。
github原文地址