Skip to content

Commit

Permalink
feat(utils): 新增 loopUntil 循环调用某个函数直至达到某个条件后返回调用结果
Browse files Browse the repository at this point in the history
  • Loading branch information
fjc0k committed Sep 24, 2021
1 parent 7ec237e commit aaa25c0
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export * from './jsonp'
export * from './keysStrict'
export * from './loadCss'
export * from './loadResource'
export * from './loopUntil'
export * from './md5'
export * from './move'
export * from './omitStrict'
Expand Down
47 changes: 47 additions & 0 deletions src/utils/loopUntil.test.ts
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)
})
})
48 changes: 48 additions & 0 deletions src/utils/loopUntil.ts
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)!,
)
}
}

0 comments on commit aaa25c0

Please sign in to comment.