this repo has no description
1import { ok, err, type Result } from '../types/result' 2 3export function debounce<T extends (...args: Parameters<T>) => void>( 4 fn: T, 5 ms: number 6): T & { cancel: () => void } { 7 let timeoutId: ReturnType<typeof setTimeout> | null = null 8 9 const debounced = ((...args: Parameters<T>) => { 10 if (timeoutId) clearTimeout(timeoutId) 11 timeoutId = setTimeout(() => { 12 fn(...args) 13 timeoutId = null 14 }, ms) 15 }) as T & { cancel: () => void } 16 17 debounced.cancel = () => { 18 if (timeoutId) { 19 clearTimeout(timeoutId) 20 timeoutId = null 21 } 22 } 23 24 return debounced 25} 26 27export function throttle<T extends (...args: Parameters<T>) => void>( 28 fn: T, 29 ms: number 30): T { 31 let lastCall = 0 32 let timeoutId: ReturnType<typeof setTimeout> | null = null 33 34 return ((...args: Parameters<T>) => { 35 const now = Date.now() 36 const remaining = ms - (now - lastCall) 37 38 if (remaining <= 0) { 39 if (timeoutId) { 40 clearTimeout(timeoutId) 41 timeoutId = null 42 } 43 lastCall = now 44 fn(...args) 45 } else if (!timeoutId) { 46 timeoutId = setTimeout(() => { 47 lastCall = Date.now() 48 timeoutId = null 49 fn(...args) 50 }, remaining) 51 } 52 }) as T 53} 54 55export function sleep(ms: number): Promise<void> { 56 return new Promise((resolve) => setTimeout(resolve, ms)) 57} 58 59export async function retry<T>( 60 fn: () => Promise<T>, 61 options: { 62 attempts?: number 63 delay?: number 64 backoff?: number 65 shouldRetry?: (error: unknown, attempt: number) => boolean 66 } = {} 67): Promise<T> { 68 const { 69 attempts = 3, 70 delay = 1000, 71 backoff = 2, 72 shouldRetry = () => true, 73 } = options 74 75 let lastError: unknown 76 let currentDelay = delay 77 78 for (let attempt = 1; attempt <= attempts; attempt++) { 79 try { 80 return await fn() 81 } catch (error) { 82 lastError = error 83 if (attempt === attempts || !shouldRetry(error, attempt)) { 84 throw error 85 } 86 await sleep(currentDelay) 87 currentDelay *= backoff 88 } 89 } 90 91 throw lastError 92} 93 94export async function retryResult<T, E>( 95 fn: () => Promise<Result<T, E>>, 96 options: { 97 attempts?: number 98 delay?: number 99 backoff?: number 100 shouldRetry?: (error: E, attempt: number) => boolean 101 } = {} 102): Promise<Result<T, E>> { 103 const { 104 attempts = 3, 105 delay = 1000, 106 backoff = 2, 107 shouldRetry = () => true, 108 } = options 109 110 let lastResult: Result<T, E> | null = null 111 let currentDelay = delay 112 113 for (let attempt = 1; attempt <= attempts; attempt++) { 114 const result = await fn() 115 lastResult = result 116 117 if (result.ok) { 118 return result 119 } 120 121 if (attempt === attempts || !shouldRetry(result.error, attempt)) { 122 return result 123 } 124 125 await sleep(currentDelay) 126 currentDelay *= backoff 127 } 128 129 return lastResult! 130} 131 132export function timeout<T>(promise: Promise<T>, ms: number): Promise<T> { 133 return new Promise((resolve, reject) => { 134 const timeoutId = setTimeout(() => { 135 reject(new Error(`Timeout after ${ms}ms`)) 136 }, ms) 137 138 promise 139 .then((value) => { 140 clearTimeout(timeoutId) 141 resolve(value) 142 }) 143 .catch((error) => { 144 clearTimeout(timeoutId) 145 reject(error) 146 }) 147 }) 148} 149 150export async function timeoutResult<T>( 151 promise: Promise<Result<T, Error>>, 152 ms: number 153): Promise<Result<T, Error>> { 154 try { 155 return await timeout(promise, ms) 156 } catch (e) { 157 return err(e instanceof Error ? e : new Error(String(e))) 158 } 159} 160 161export async function parallel<T>( 162 tasks: (() => Promise<T>)[], 163 concurrency: number 164): Promise<T[]> { 165 const results: T[] = [] 166 const executing: Promise<void>[] = [] 167 168 for (const task of tasks) { 169 const p = task().then((result) => { 170 results.push(result) 171 }) 172 173 executing.push(p) 174 175 if (executing.length >= concurrency) { 176 await Promise.race(executing) 177 executing.splice( 178 executing.findIndex((e) => e === p), 179 1 180 ) 181 } 182 } 183 184 await Promise.all(executing) 185 return results 186} 187 188export async function mapParallel<T, U>( 189 items: T[], 190 fn: (item: T, index: number) => Promise<U>, 191 concurrency: number 192): Promise<U[]> { 193 const results: U[] = new Array(items.length) 194 const executing: Promise<void>[] = [] 195 196 for (let i = 0; i < items.length; i++) { 197 const index = i 198 const p = fn(items[index], index).then((result) => { 199 results[index] = result 200 }) 201 202 executing.push(p) 203 204 if (executing.length >= concurrency) { 205 await Promise.race(executing) 206 const doneIndex = executing.findIndex( 207 (e) => 208 (e as Promise<void> & { _done?: boolean })._done !== false 209 ) 210 if (doneIndex >= 0) { 211 executing.splice(doneIndex, 1) 212 } 213 } 214 } 215 216 await Promise.all(executing) 217 return results 218} 219 220export function createAbortable<T>( 221 fn: (signal: AbortSignal) => Promise<T> 222): { promise: Promise<T>; abort: () => void } { 223 const controller = new AbortController() 224 return { 225 promise: fn(controller.signal), 226 abort: () => controller.abort(), 227 } 228} 229 230export interface Deferred<T> { 231 promise: Promise<T> 232 resolve: (value: T) => void 233 reject: (error: unknown) => void 234} 235 236export function deferred<T>(): Deferred<T> { 237 let resolve!: (value: T) => void 238 let reject!: (error: unknown) => void 239 240 const promise = new Promise<T>((res, rej) => { 241 resolve = res 242 reject = rej 243 }) 244 245 return { promise, resolve, reject } 246}