-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(utils): 新增 loopUntil 循环调用某个函数直至达到某个条件后返回调用结果
- Loading branch information
Showing
3 changed files
with
96 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { loopUntil, LoopUntilRetryLimitExceededError } from './loopUntil' | ||
|
||
describe('loopUntil', () => { | ||
test('同步正常', async () => { | ||
let i = 0 | ||
const getNumber = () => i++ | ||
expect( | ||
await loopUntil( | ||
() => getNumber(), | ||
num => num === 10, | ||
{ | ||
retryDelay: 0, | ||
}, | ||
), | ||
).toBe(10) | ||
}) | ||
|
||
test('异步正常', async () => { | ||
let i = 0 | ||
const getNumberAsync = async () => i++ | ||
expect( | ||
await loopUntil( | ||
() => getNumberAsync(), | ||
async num => num === 10, | ||
{ | ||
retryDelay: 0, | ||
}, | ||
), | ||
).toBe(10) | ||
}) | ||
|
||
test('重试次数限制', async () => { | ||
let i = 0 | ||
const getNumberAsync = async () => i++ | ||
await expect( | ||
loopUntil( | ||
() => getNumberAsync(), | ||
num => num === 10, | ||
{ | ||
retryDelay: 0, | ||
retryLimit: 5, | ||
}, | ||
), | ||
).rejects.toBeInstanceOf(LoopUntilRetryLimitExceededError) | ||
expect(i).toBe(6) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { AsyncOrSync, OneOrMore } from '../types' | ||
import { sample } from 'lodash-uni' | ||
import { wait } from './wait' | ||
|
||
export interface LoopUntilOptions { | ||
/** | ||
* 重试延时,为数组时随机挑选一个。 | ||
*/ | ||
retryDelay: OneOrMore<number> | ||
|
||
/** | ||
* 重试限制。 | ||
*/ | ||
retryLimit?: number | ||
} | ||
|
||
export class LoopUntilRetryLimitExceededError extends Error {} | ||
|
||
/** | ||
* 循环调用某个函数直至达到某个条件后返回调用结果。 | ||
* | ||
* @param fn 要调用的函数 | ||
* @param condition 条件 | ||
* @param options 选项 | ||
*/ | ||
export async function loopUntil<T>( | ||
fn: () => AsyncOrSync<T>, | ||
condition: (res: T) => AsyncOrSync<boolean>, | ||
options: LoopUntilOptions, | ||
): Promise<T> { | ||
let retryCount = 0 | ||
// eslint-disable-next-line no-constant-condition | ||
while (true) { | ||
const res = await fn() | ||
if (await condition(res)) { | ||
return res | ||
} | ||
if (options.retryLimit && retryCount >= options.retryLimit) { | ||
throw new LoopUntilRetryLimitExceededError('已达到最大重试次数') | ||
} | ||
retryCount++ | ||
await wait( | ||
typeof options.retryDelay === 'number' | ||
? options.retryDelay | ||
: sample(options.retryDelay)!, | ||
) | ||
} | ||
} |