From 9a0e9b7ebe1497df041a278ab0f2180aa79ffb4c Mon Sep 17 00:00:00 2001 From: Utopia Date: Sun, 26 Feb 2023 00:10:36 +0800 Subject: [PATCH] feat: new function - onlyResolvesLast --- README.md | 1 + packages/core/src/index.ts | 2 ++ packages/core/src/onlyResolvesLast.test.ts | 37 +++++++++++++++++++++ packages/core/src/onlyResolvesLast.ts | 38 ++++++++++++++++++++++ packages/core/src/retry.test.ts | 2 +- 5 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/onlyResolvesLast.test.ts create mode 100644 packages/core/src/onlyResolvesLast.ts diff --git a/README.md b/README.md index d774a47..2a46fe9 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,7 @@ pnpm add @utopia-utils/dom * deepEqual: 深拷贝。[source](https://github.com/GreatAuk/utopia-utils/blob/main/packages/core/src/deepEqual.ts) * compose: 函数组合, 从右到左执行。[source](https://github.com/GreatAuk/utopia-utils/blob/main/packages/core/src/compose.ts) * pipe: 函数组合, 从左到右执行。[source](https://github.com/GreatAuk/utopia-utils/blob/main/packages/core/src/pipe.ts) +* onlyResolvesLast: 解决竞态问题,只保留最后一次调用的结果。[source](https://github.com/GreatAuk/utopia-utils/blob/main/packages/core/src/onlyResolvesLast.ts) ### 类型判断 ```bash diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index b75e90e..d2330f9 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -21,6 +21,8 @@ export * from './math' export * from './memoize' export * from './objectKeys' export * from './omit' +export * from './once' +export * from './onlyResolvesLast' export * from './pick' export * from './pipe' export * from './randomInt' diff --git a/packages/core/src/onlyResolvesLast.test.ts b/packages/core/src/onlyResolvesLast.test.ts new file mode 100644 index 0000000..416c293 --- /dev/null +++ b/packages/core/src/onlyResolvesLast.test.ts @@ -0,0 +1,37 @@ +import { onlyResolvesLast } from './onlyResolvesLast' + +describe('onlyResolvesLast', () => { + it('should resolve the last call', async () => { + const foo_ = async (n: number) => { + return new Promise((resolve) => { + setTimeout(() => resolve(n), Math.random()) + }) + } + const foo = onlyResolvesLast(foo_) + let lastValue = 0 + + await Promise.race([ + foo(1).then(v => lastValue = v), + foo(2).then(v => lastValue = v), + foo(3).then(v => lastValue = v), + foo(4).then(v => lastValue = v), + ]) + expect(lastValue).toBe(4) + }) + it('should reject the last call', async () => { + const foo_ = async (n: number) => { + return new Promise((resolve, reject) => { + setTimeout(() => reject(n), Math.random()) + }) + } + const foo = onlyResolvesLast(foo_) + let lastError = 0 + + await Promise.race([ + foo(1).catch(e => lastError = e), + foo(2).catch(e => lastError = e), + foo(3).catch(e => lastError = e), + ]) + expect(lastError).toBe(3) + }) +}) diff --git a/packages/core/src/onlyResolvesLast.ts b/packages/core/src/onlyResolvesLast.ts new file mode 100644 index 0000000..54bb5b8 --- /dev/null +++ b/packages/core/src/onlyResolvesLast.ts @@ -0,0 +1,38 @@ +/** + * Wraps an async function, and ensures that only last call will ever resolve/reject + * @param {T} fn - T - the function to wrap + * @returns A function that will only resolve the last call. + * @example + * ``` + const foo_ = (id: number) => fetch('/api/post/' + id) + const foo = onlyResolvesLast(foo) + foo(1).then(() => console.log('resolved')) // promise will never resolve + foo(2).then(() => console.log('resolved')) // promise will never resolve + foo(3).then(() => console.log('resolved')) // promise will resolve with response + * ``` + */ +export function onlyResolvesLast Promise>(fn: T) { + let time = 0 + function wrappedFn(this: ThisParameterType, ...args: Parameters): ReturnType { + const currentTime = time + 1 + time = currentTime + + const res = fn.apply(this, args) + + return new Promise((resolve, reject) => { + Promise.resolve(res) + .then((res_) => { + // just resolve last call + if (currentTime === time) + resolve(res_) + }) + .catch((err) => { + // just resolve last call + if (currentTime === time) + reject(err) + }) + }) as any + } + + return wrappedFn +} diff --git a/packages/core/src/retry.test.ts b/packages/core/src/retry.test.ts index 859d5d5..55d4790 100644 --- a/packages/core/src/retry.test.ts +++ b/packages/core/src/retry.test.ts @@ -73,7 +73,7 @@ describe('retry', async () => { expect(res).toBe(null) expect(callNum).toBe(3) // 1 * 5 + 2 * 5 - expect(endTime - startTime).toBeGreaterThanOrEqual(15) + expect(endTime - startTime).toBeGreaterThanOrEqual(14) }) it('should retry the function with delay (function return promise)', async () => { let callNum = 0