lodash已死?Radash库方法介绍及源码解析 —— 异步方法篇

写在前面
tips:点赞 + 收藏 = 学会!
- 我们前面已经介绍了
radash的相关信息和所有Array相关方法,详情可前往主页查看。 - 本篇我们继续介绍radash中异步相关的方法。
- 所有方法分享完毕后,后续作者也会整理出
Radash库所有方法的使用目录,包括文章说明和脑图说明。- 因为方法较多,后续将专门发布一篇总结文档,方便大家查阅使用。
- 所有方法的思维导图说明后续也会上传至 github 和 gitee,有需要的可以访问下载。
all:同时执行多个异步操作
- 使用说明
- 功能描述: 类似于
Promise.all和Promise.allSettled,等待一个由多个Promise组成的对象或数组中的所有Promise都完成(或者其中一个失败)。执行的所有错误和抛出的错误都会收集在AggregateError中。 - 参数:promise对象/promise数组
- 返回值:所有
promise执行后的结果数组或对象
- 功能描述: 类似于
- 使用代码示例
import { all } from 'radash' // 传入promise数组
const [user] = await all([
api.users.create(...),
s3.buckets.create(...),
slack.customerSuccessChannel.sendMessage(...)
]) // 传入对象
const { user } = await all({
user: api.users.create(...),
bucket: s3.buckets.create(...),
message: slack.customerSuccessChannel.sendMessage(...)
})
- 源码解析
// 定义一个泛型异步函数 `all`。
export async function all<
// 泛型约束 `T` 可以是一个 `Promise` 数组或一个 `Promise` 对象。
T extends Record<string, Promise<any>> | Promise<any>[]
>(promises: T) {
// 根据 `promises` 是数组还是对象,将其转换成一个统一格式的数组 `entries`。
const entries = isArray(promises)
? promises.map(p => [null, p] as [null, Promise<any>])
: Object.entries(promises) // 使用 `Promise.all` 等待所有 `Promise` 完成,并处理每个 `Promise` 的结果和异常。
const results = await Promise.all(
entries.map(([key, value]) =>
value
.then(result => ({ result, exc: null, key })) // 如果成功,记录结果。
.catch(exc => ({ result: null, exc, key })) // 如果失败,记录异常。
)
) // 筛选出所有出现异常的结果。
const exceptions = results.filter(r => r.exc)
// 如果有异常,抛出一个 `AggregateError`,包含所有异常。
if (exceptions.length > 0) {
throw new AggregateError(exceptions.map(e => e.exc))
} // 如果输入的 `promises` 是数组,返回一个包含所有结果的数组。
if (isArray(promises)) {
return results.map(r => r.result) as T extends Promise<any>[]
? PromiseValues<T>
: unknown
} // 如果输入的 `promises` 是对象,将结果组合成一个新对象并返回。
return results.reduce(
(acc, item) => ({
...acc,
[item.key!]: item.result // 使用断言 `item.key!`,因为我们知道 `key` 不会是 `null`。
}),
{} as { [K in keyof T]: Awaited<T[K]> } // 返回类型是一个对象,其键类型为 `T` 的键,值类型为 `T` 中 `Promise` 解析后的类型。
)
}
- 方法流程说明:
- 将输入的
promises转换为一个统一格式的entries数组,无论它是一个Promise数组还是一个Promise对象。 - 对于每个
entry,创建一个新的Promise来处理成功和失败的情况,并使用Promise.all等待所有这些新Promise完成。 - 如果所有
Promise都成功解析,根据promises是数组还是对象,返回一个包含所有结果的数组或对象。 - 如果有一个或多个
Promise失败,则抛出一个AggregateError,其中包含所有失败的Promise的异常。
- 将输入的
- 方法流程说明:
defer:在异步流程中添加清理或错误处理逻辑
- 使用说明
- 功能描述:用来执行一个异步函数,同时提供注册回调的机制,在异步函数执行完成后执行特定回调操作。
- 参数:异步函数。
- 返回值:异步函数成功执行时,返回其响应结果,否则重新抛出错误。
- 使用代码示例
import { defer } from 'radash' await defer(async (cleanup) => {
const buildDir = await createBuildDir() cleanup(() => fs.unlink(buildDir)) await build()
}) await defer(async (register) => {
const org = await api.org.create()
register(async () => api.org.delete(org.id), { rethrow: true }) const user = await api.user.create()
register(async () => api.users.delete(user.id), { rethrow: true }) await executeTest(org, user)
})
- 源码解析
// 定义一个异步泛型函数 `defer`。
export const defer = async <TResponse>(
// `func` 是一个接受注册函数 `register` 的异步函数。
func: (
register: (
// `register` 允许 `func` 注册一个回调函数 `fn`,该函数在 `func` 执行完成后调用。
// 可以通过 `options` 指定是否在回调函数中重新抛出错误。
fn: (error?: any) => any,
options?: { rethrow?: boolean }
) => void
) => Promise<TResponse>
): Promise<TResponse> => {
// 初始化一个用于存放回调函数及其选项的数组 `callbacks`。
const callbacks: {
fn: (error?: any) => any
rethrow: boolean
}[] = [] // 实现注册函数 `register`,它将回调函数及其选项添加到 `callbacks` 数组。
const register = (
fn: (error?: any) => any,
options?: { rethrow?: boolean }
) =>
callbacks.push({
fn,
rethrow: options?.rethrow ?? false
}) // 调用 `tryit` 函数执行 `func`,并传入 `register`。
// `tryit` 函数不在提供的代码片段中,但我们可以假设它是一个错误处理函数,返回一个包含错误和响应的元组。
const [err, response] = await tryit(func)(register) // 遍历 `callbacks` 数组,依次执行每个回调函数。
for (const { fn, rethrow } of callbacks) {
// 使用 `tryit` 函数调用回调,以捕获并处理任何抛出的错误。
const [rethrown] = await tryit(fn)(err)
// 如果回调函数中有错误被重新抛出,并且 `rethrow` 选项为 `true`,则重新抛出该错误。
if (rethrown && rethrow) throw rethrown
} // 如果 `func` 执行时有错误产生,重新抛出该错误。
if (err) throw err
// 如果 `func` 执行成功,返回响应结果。
return response
}
- 方法流程说明:
- 定义一个
callbacks数组来存储注册的回调函数及其选项。 - 实现
register函数,该函数允许func注册回调函数和选项。 - 调用外部提供的(但在代码片段中未定义)
tryit函数执行func,并传递register函数给func。 - 等待
func完成执行,获取可能的错误err和响应response。 - 依次执行
callbacks数组中的回调函数,处理可能的错误。 - 如果任何一个回调函数中出现需要重新抛出的错误,并且其
rethrow选项为true,则重新抛出该错误。 - 如果
func执行时产生了错误,重新抛出该错误。 - 如果
func成功执行,返回其响应结果。
- 定义一个
- 方法流程说明:
guard:执行一个函数,并提供错误处理的能力
- 使用说明
- 功能描述:
guard函数可以用来为函数调用提供额外的错误处理逻辑,特别是当你希望根据错误类型选择性地处理错误时。 - 参数:目标函数、指定错误对象得函数(可选)。
- 返回值:抛出原始或返回undefined。
- 功能描述:
- 使用代码示例
import { guard } from 'radash' const users = (await guard(fetchUsers)) ?? [] const isInvalidUserError = (err: any) => err.code === 'INVALID_ID'
const user = (await guard(fetchUser, isInvalidUserError)) ?? DEFAULT_USER
- 源码解析
// 定义一个泛型函数 `guard`。
export const guard = <TFunction extends () => any>(
// 参数 `func` 是一个无参数的函数,它可能返回任何类型的值,包括 `Promise`。
func: TFunction,
// 可选参数 `shouldGuard` 是一个函数,它接受一个错误对象 `err`,
// 并返回一个布尔值,指示是否应该 "guard" 这个错误。
shouldGuard?: (err: any) => boolean
// 函数的返回类型依赖于 `func` 的返回类型。如果 `func` 返回一个 `Promise`,
// 则 `guard` 返回一个 `Promise`,该 `Promise` 解析为 `func` 的返回值或 `undefined`。
// 如果 `func` 不返回 `Promise`,则 `guard` 返回 `func` 的返回值或 `undefined`。
): ReturnType<TFunction> extends Promise<any>
? Promise<Awaited<ReturnType<TFunction>> | undefined>
: ReturnType<TFunction> | undefined => {
// 定义一个内部函数 `_guard`,它接受一个错误对象 `err`。
const _guard = (err: any) => {
// 如果提供了 `shouldGuard` 函数并且该函数返回 `false`,
// 表示不应该 "guard" 这个错误,则重新抛出该错误。
if (shouldGuard && !shouldGuard(err)) throw err
// 否则,返回 `undefined`。
return undefined as any
} // 定义一个类型守卫函数 `isPromise`,它检查一个值是否为 `Promise`。
const isPromise = (result: any): result is Promise<any> =>
result instanceof Promise try {
// 尝试执行 `func` 并获取结果。
const result = func()
// 如果 `result` 是一个 `Promise`,使用 `catch` 方法应用 `_guard` 函数。
// 否则,直接返回 `result`。
return isPromise(result) ? result.catch(_guard) : result
} catch (err) {
// 如果在执行 `func` 时抛出错误,使用 `_guard` 函数处理该错误。
return _guard(err)
}
}
- 方法流程说明:
- 尝试执行
func函数并捕获任何抛出的错误。 - 如果
func执行成功并返回一个Promise,那么使用catch方法捕获该Promise可能抛出的错误,并应用_guard函数。 - 如果
func执行成功并没有返回Promise,那么直接返回结果。 - 如果
func抛出错误,应用_guard函数来决定是否重新抛出错误或返回undefined。 - 如果提供了
shouldGuard函数,它将用来判断是否应该 "guard"(捕获并返回undefined)错误。如果shouldGuard函数返回false,则抛出原始错误;如果返回true或未提供shouldGuard函数,则返回undefined。
- 尝试执行
- 方法流程说明:
map:对一个数组中的每个元素执行一个异步映射函数
- 使用说明
- 功能描述:它用于对一个数组中的每个元素执行一个异步映射函数,并返回一个包含所有映射结果的新数组。这个函数是
Array.prototype.map方法的异步版本。 - 参数:数组,异步函数。
- 返回值:映射后的新数组。
- 功能描述:它用于对一个数组中的每个元素执行一个异步映射函数,并返回一个包含所有映射结果的新数组。这个函数是
- 使用代码示例
import { map } from 'radash' const userIds = [1, 2, 3, 4] const users = await map(userIds, async (userId) => {
return await api.users.find(userId)
})
- 源码解析
// 定义一个异步函数 `map`。
export const map = async <T, K>(
// 第一个参数 `array` 是一个具有只读属性的泛型数组。
array: readonly T[],
// 第二个参数 `asyncMapFunc` 是一个异步映射函数,它接受一个数组元素和它的索引,
// 返回一个 `Promise`,该 `Promise` 解析为新类型 `K` 的值。
asyncMapFunc: (item: T, index: number) => Promise<K>
): Promise<K[]> => {
// 如果传入的数组 `array` 不存在,则返回一个空数组。
if (!array) return []
// 初始化一个空数组 `result`,用于存放映射后的新值。
let result = []
// 初始化一个索引计数器 `index`。
let index = 0
// 使用 `for...of` 循环遍历数组 `array` 的每个元素。
for (const value of array) {
// 对每个元素调用 `asyncMapFunc` 映射函数,并等待其 `Promise` 解析。
const newValue = await asyncMapFunc(value, index++)
// 将解析后的新值添加到 `result` 数组中。
result.push(newValue)
}
// 循环完成后,返回包含所有新值的数组 `result`。
return result
}
- 方法流程说明:
- 检查
array是否存在。如果不存在,返回一个空数组。 - 初始化一个空数组
result用于存储映射结果,以及一个索引计数器index。 - 遍历
array中的每个元素。 - 对于每个元素,调用异步映射函数
asyncMapFunc并等待Promise解析。 - 将异步映射函数解析后的结果添加到
result数组中。 - 在遍历完所有元素之后,返回包含所有映射结果的
result数组。
- 检查
- 方法流程说明:
parallel:并行地处理数组中的元素,并对每个元素执行一个异步函数
- 使用说明
- 功能描述:这个函数会限制同时进行的异步操作的数量,以避免同时启动过多的异步任务。
- 参数:限制数量(number)、需要被异步处理的元素数组、转换函数(将数组中的每个元素转换为一个异步操作)。
- 返回值:返回一个数组,该数组包含了按原数组顺序排序的所有成功的结果。
- 使用代码示例
// 定义一个异步泛型函数 `parallel`。
export const parallel = async <T, K>(
// `limit` 是一个数字,指定了可以同时运行的异步任务的最大数量。
limit: number,
// `array` 是一个只读数组,包含将要被异步处理的元素。
array: readonly T[],
// `func` 是一个函数,将数组中的每个元素转换为一个异步操作(返回 Promise)。
func: (item: T) => Promise<K>
): Promise<K[]> => {
// 将数组 `array` 转换为包含元素和它们索引的对象的数组 `work`。
const work = array.map((item, index) => ({
index,
item
}))
// 定义一个处理函数 `processor`,它将异步处理 `work` 数组中的元素。
const processor = async (res: (value: WorkItemResult<K>[]) => void) => {
const results: WorkItemResult<K>[] = []
while (true) {
// 从 `work` 数组的末尾取出一个元素进行处理。
const next = work.pop()
// 如果没有更多元素,调用回调函数 `res` 并传入结果数组 `results`。
if (!next) return res(results)
// 使用 `tryit` 函数执行 `func` 并处理结果或错误。
const [error, result] = await tryit(func)(next.item)
// 将结果或错误添加到 `results` 数组中。
results.push({
error,
result: result as K,
index: next.index
})
}
}
// 创建一个 `queues` 数组,它包含了 `limit` 个新的 Promise,每个 Promise 都由 `processor` 函数处理。
const queues = list(1, limit).map(() => new Promise(processor))
// 使用 `Promise.all` 等待所有的 `queues` 中的 Promise 完成。
const itemResults = (await Promise.all(queues)) as WorkItemResult<K>[][]
// 将所有的结果扁平化并根据索引排序,然后使用 `fork` 函数将结果分为错误和成功的结果。
const [errors, results] = fork(
sort(itemResults.flat(), r => r.index),
x => !!x.error
)
// 如果有任何错误,抛出一个 `AggregateError` 包含所有错误。
if (errors.length > 0) {
throw new AggregateError(errors.map(error => error.error))
}
// 返回一个数组,它包含了按原数组顺序排序的所有成功的结果。
return results.map(r => r.result)
}
- 源码解析
// 定义一个异步泛型函数 `parallel`。
export const parallel = async <T, K>(
// `limit` 是一个数字,指定了可以同时运行的异步任务的最大数量。
limit: number,
// `array` 是一个只读数组,包含将要被异步处理的元素。
array: readonly T[],
// `func` 是一个函数,将数组中的每个元素转换为一个异步操作(返回 Promise)。
func: (item: T) => Promise<K>
): Promise<K[]> => {
// 将数组 `array` 转换为包含元素和它们索引的对象的数组 `work`。
const work = array.map((item, index) => ({
index,
item
}))
// 定义一个处理函数 `processor`,它将异步处理 `work` 数组中的元素。
const processor = async (res: (value: WorkItemResult<K>[]) => void) => {
const results: WorkItemResult<K>[] = []
while (true) {
// 从 `work` 数组的末尾取出一个元素进行处理。
const next = work.pop()
// 如果没有更多元素,调用回调函数 `res` 并传入结果数组 `results`。
if (!next) return res(results)
// 使用 `tryit` 函数执行 `func` 并处理结果或错误。
const [error, result] = await tryit(func)(next.item)
// 将结果或错误添加到 `results` 数组中。
results.push({
error,
result: result as K,
index: next.index
})
}
}
// 创建一个 `queues` 数组,它包含了 `limit` 个新的 Promise,每个 Promise 都由 `processor` 函数处理。
const queues = list(1, limit).map(() => new Promise(processor))
// 使用 `Promise.all` 等待所有的 `queues` 中的 Promise 完成。
const itemResults = (await Promise.all(queues)) as WorkItemResult<K>[][]
// 将所有的结果扁平化并根据索引排序,然后使用 `fork` 函数将结果分为错误和成功的结果。
const [errors, results] = fork(
sort(itemResults.flat(), r => r.index),
x => !!x.error
)
// 如果有任何错误,抛出一个 `AggregateError` 包含所有错误。
if (errors.length > 0) {
throw new AggregateError(errors.map(error => error.error))
}
// 返回一个数组,它包含了按原数组顺序排序的所有成功的结果。
return results.map(r => r.result)
}
- 这段代码中使用了几个未定义的函数和类型,如
tryit、list、fork和sort,以及类型WorkItemResult<K>。我们可以假设这些函数和类型具有以下功能:tryit(func)(item):执行func(item)并捕获任何抛出的错误,返回一个包含错误和结果的元组。list(1, limit):创建一个包含从 1 到limit的数字的数组。fork(array, condition):分割数组array,根据condition函数返回的布尔值将数组分为包含错误的元素和成功的元素两个数组。sort(array, keySelector):根据keySelector函数返回的键对数组array进行排序。WorkItemResult<K>:一个类型,表示工作项的结果,包含可能的error、成功的result以及元素的index。
- 这段代码中使用了几个未定义的函数和类型,如
reduce:对数组中的每个元素执行一个异步归约函数
- 使用说明
- 功能描述:它是
Array.prototype.reduce方法的异步版本,用于对数组中的每个元素执行一个异步归约函数,并返回最终的归约值。 - 参数:被归约处理的元素数组、异步归约函数。
- 返回值:返回最终归约的值。
- 功能描述:它是
- 使用代码示例
import { reduce } from 'radash' const userIds = [1, 2, 3, 4] const users = await reduce(userIds, async (acc, userId) => {
const user = await api.users.find(userId)
return {
...acc,
[userId]: user
}
}, {})
- 源码解析
// 定义一个异步泛型函数 `reduce`。
export const reduce = async <T, K>(
// 第一个参数 `array` 是一个只读数组,包含将要被归约处理的元素。
array: readonly T[],
// 第二个参数 `asyncReducer` 是一个异步归约函数,它接受累加值 `acc`、当前元素 `item` 和它的索引 `index`,
// 并返回一个 `Promise`,该 `Promise` 解析为新的累加值。
asyncReducer: (acc: K, item: T, index: number) => Promise<K>,
// 第三个参数 `initValue` 是可选的初始值。
initValue?: K
): Promise<K> => {
// 检查初始值是否提供了。
const initProvided = initValue !== undefined
// 如果没有提供初始值且数组为空,则抛出错误。
if (!initProvided && array?.length < 1) {
throw new Error('Cannot reduce empty array with no init value')
}
// 如果提供了初始值,使用整个数组;否则,从数组的第二个元素开始迭代。
const iter = initProvided ? array : array.slice(1)
// 初始化累加值 `value`。如果提供了初始值,使用它;否则使用数组的第一个元素。
let value: any = initProvided ? initValue : array[0]
// 使用 `for...of` 循环和 `entries` 方法遍历数组或其子数组。
for (const [i, item] of iter.entries()) {
// 对每个元素调用异步归约函数 `asyncReducer` 并等待其 `Promise` 解析。
value = await asyncReducer(value, item, i)
}
// 循环完成后,返回最终的累加值 `value`。
return value
}
- 方法流程说明:
- 检查是否提供了初始值
initValue。 - 如果没有提供初始值且数组为空,则抛出错误,因为无法从空数组中归约出一个值。
- 确定迭代的数组。如果提供了初始值,则迭代整个数组;如果没有提供初始值,则从数组的第二个元素开始迭代。
- 初始化累加值
value。如果提供了初始值,则使用该初始值;如果没有提供初始值,则使用数组的第一个元素作为初始累加值。 - 遍历数组,对每个元素调用异步归约函数
asyncReducer,并等待其返回的Promise解析。 - 更新累加值
value为asyncReducer返回的新值。 - 在遍历完所有元素之后,返回最终的累加值。
- 检查是否提供了初始值
- 方法流程说明:
retry:反复尝试执行一个异步操作,直到达到设置上限
- 使用说明
- 功能描述:用于反复尝试执行一个异步操作,直到成功或达到重试次数上限。如果操作失败,可以选择在重试之间设置延迟或使用退避函数(backoff)来计算延迟时间。
- 参数:条件对象options(包含:重复次数、延迟、退避函数)、失败执行的异步操作函数。
- 返回值:可能发挥undefined。
- 使用代码示例
import { retry } from 'radash' await retry({}, api.users.list)
await retry({ times: 10 }, api.users.list)
await retry({ times: 2, delay: 1000 }, api.users.list) // exponential backoff
await retry({ backoff: i => 10**i }, api.users.list)
- 源码解析
// 定义一个异步泛型函数 `retry`。
export const retry = async <TResponse>(
// `options` 对象包含重试策略的选项。
options: {
times?: number // 重试次数,默认为 3。
delay?: number | null // 固定延迟时间,如果提供,则在重试之间等待这么多毫秒。
backoff?: (count: number) => number // 退避函数,可以根据重试次数来计算延迟时间。
},
// `func` 是要执行的异步函数,它可能会失败。
func: (exit: (err: any) => void) => Promise<TResponse>
): Promise<TResponse> => {
// 从 `options` 中获取重试次数、固定延迟时间和退避函数。
const times = options?.times ?? 3
const delay = options?.delay
const backoff = options?.backoff ?? null // 使用 `range` 函数生成一个序列,并遍历这个序列进行重试。
for (const i of range(1, times)) {
// 尝试执行 `func` 函数,并捕获可能的错误 `err` 和结果 `result`。
const [err, result] = (await tryit(func)((err: any) => {
// 如果 `func` 失败,并使用 `exit` 函数退出,则抛出一个特殊的错误对象。
throw { _exited: err }
})) as [any, TResponse] // 如果没有错误,说明 `func` 成功执行,返回结果。
if (!err) return result // 如果有特殊的退出错误,重新抛出原始错误。
if (err._exited) throw err._exited // 如果是最后一次重试且仍然失败,抛出错误。
if (i === times) throw err // 如果设置了固定延迟时间,使用 `sleep` 函数等待。
if (delay) await sleep(delay) // 如果提供了退避函数,根据重试次数计算延迟时间并等待。
if (backoff) await sleep(backoff(i))
} // 如果代码执行到这里,说明逻辑上不应该到达的代码路径。
// 这是为了满足 TypeScript 的严格模式要求。
/* istanbul ignore next */
return undefined as unknown as TResponse
}
- 方法流程说明:
- 从
options中获取重试次数、延迟和退避函数。 - 遍历从 1 到重试次数的范围。
- 在每次迭代中,尝试执行
func并捕获可能的错误和结果。 - 如果
func成功执行(没有错误),返回结果。 - 如果有错误,并且是通过
exit函数显式退出的,重新抛出原始错误。 - 如果达到了重试次数上限并且仍然失败,抛出最后一次的错误。
- 如果指定了延迟或退避函数,根据相应的策略等待一段时间后再重试。
- 如果执行到函数的末尾,返回
undefined作为占位符,因为逻辑上不应该到达这里。
- 从
- 方法流程说明:
sleep:提供一个延时机制
- 使用说明
- 功能描述:提供一个延时机制,通常用于异步操作中的暂停。
- 参数:暂停时间(ms)。
- 返回值:返回一个新的Promise。
- 使用代码示例
import { sleep } from 'radash' await sleep(2000) // => waits 2 seconds
- 源码解析
// 定义一个名为 `sleep` 的函数。
export const sleep = (milliseconds: number) => {
// 返回一个新的 Promise。
return new Promise(res =>
// 使用 `setTimeout` 函数设置一个定时器,它在 `milliseconds` 指定的毫秒数后执行。
setTimeout(
// 当定时器到时,调用 `res` 函数来解析这个 Promise。
res,
// 传递给 `setTimeout` 的毫秒数,它决定了延时的长度。
milliseconds
)
)
}
- 方法流程说明:当你调用
sleep函数并传入一个毫秒数时,它会返回一个Promise。这个Promise不会立即解析,而是会等待你指定的时间长度。当时间到了之后,Promise会被解析,然后你可以在.then()方法中继续执行后续的代码,或者你可以在async函数中使用await关键字来等待Promise解析。
- 方法流程说明:当你调用
tryit:捕获函数在执行过程中可能抛出的同步或异步错误
- 使用说明
- 功能描述:
tryit是一个高阶函数。用于捕获函数在执行过程中可能抛出的同步或异步错误,并返回一个元组,其中包含错误对象或函数的返回值。这个函数的目的是提供一种安全执行任意函数并处理错误的方式。 - 参数:需要被捕获的函数。
- 返回值:返回一个新函数,该函数接收与传入函数相同的参数。
- 功能描述:
- 使用代码示例
import { tryit } from 'radash' const findUser = tryit(api.users.find) const [err, user] = await findUser(userId)
- 源码解析
// 定义一个泛型高阶函数 `tryit`。
export const tryit = <Args extends any[], Return>(
// `func` 是一个接受任意参数的函数,其返回值可以是任何类型,包括 `Promise`。
func: (...args: Args) => Return
) => {
// 返回一个新函数,这个新函数接受与 `func` 相同的参数。
return (
...args: Args
// 新函数的返回类型取决于 `func` 的返回类型是否是 `Promise`。
// 如果 `func` 返回 `Promise`,则返回一个 `Promise`,包含一个错误或函数返回值的元组。
// 如果 `func` 返回非 `Promise`,则直接返回错误或函数返回值的元组。
): Return extends Promise<any>
? Promise<[Error, undefined] | [undefined, Awaited<Return>]>
: [Error, undefined] | [undefined, Return] => {
try {
// 尝试执行 `func` 并获取结果。
const result = func(...args)
// 使用辅助函数 `isPromise` 检查 `result` 是否是 `Promise`。
if (isPromise(result)) {
// 如果是 `Promise`,使用 `then` 和 `catch` 方法处理结果或捕获错误。
return result
.then(value => [undefined, value]) // 成功时返回值的元组。
.catch(err => [err, undefined]) // 错误时返回错误的元组。
}
// 如果 `result` 不是 `Promise`,直接返回值的元组。
return [undefined, result]
} catch (err) {
// 如果执行 `func` 时捕获到同步错误,返回错误的元组。
return [err as any, undefined]
}
}
}
- 方法流程说明:
tryit函数接受一个函数func作为参数。tryit返回一个新函数,这个新函数接受与func相同的参数。- 当调用这个新函数时,它尝试执行
func。 - 如果
func成功执行,且其返回值不是Promise,新函数返回一个元组[undefined, result]。 - 如果
func返回一个Promise,新函数返回一个Promise,该Promise解析为元组[undefined, value]或[err, undefined],具体取决于Promise是成功解析还是被拒绝。 - 如果在执行
func时捕获到同步错误,新函数返回一个元组[err, undefined]。 - 如果
func的返回类型是Promise,那么新函数的返回类型也是Promise,否则返回类型就是元组。
- 方法流程说明:
写在后面
- 后续我们会继续分享
Radash库中其他方法的使用和源码解析。 - 大家有任何问题或见解,欢迎评论区留言交流和批评指正!!!
- 你的每一个点赞和收藏都是作者写作的动力!!!
- 点击访问:radash官网
lodash已死?Radash库方法介绍及源码解析 —— 异步方法篇的更多相关文章
- Android IntentService使用介绍以及源码解析
版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.IntentService概述及使用举例 IntentService内部实现机制用到了HandlerThread,如果对HandlerThrea ...
- IPerf——网络测试工具介绍与源码解析(4)
上篇随笔讲到了TCP模式下的客户端,接下来会讲一下TCP模式普通场景下的服务端,说普通场景则是暂时不考虑双向测试的可能,毕竟了解一项东西还是先从简单的情况下入手会快些. 对于服务端,并不是我们认为的直 ...
- ReentrantLock介绍及源码解析
ReentrantLock介绍及源码解析 一.ReentrantLock介绍 ReentrantLock是JUC包下的一个并发工具类,可以通过他显示的加锁(lock)和释放锁(unlock)来实现线程 ...
- IPerf——网络测试工具介绍与源码解析(2)
对于IPerf源码解析,我是基于2.0.5版本在Windows下执行的情况进行分析的,提倡开始先通过对源码的简单修改使其能够在本地编译器运行起来,这样可以打印输出一些中间信息,对于理解源码的逻辑,程序 ...
- vue系列---Mustache.js模板引擎介绍及源码解析(十)
mustache.js(3.0.0版本) 是一个javascript前端模板引擎.官方文档(https://github.com/janl/mustache.js) 根据官方介绍:Mustache可以 ...
- 【转载】Android IntentService使用全面介绍及源码解析
一 IntentService介绍 IntentService定义的三个基本点:是什么?怎么用?如何work? 官方解释如下: //IntentService定义的三个基本点:是什么?怎么用?如何wo ...
- Java集合 - List介绍及源码解析
(源码版本为 JDK 8) 集合类在java.util包中,类型大体可以分为3种:Set.List.Map. JAVA 集合关系(简图) (图片来源网络) List集合和Set集合都是继承Collec ...
- IPerf——网络测试工具介绍与源码解析(1)
IPerf是一个开源的测试网络宽带并能统计并报告延迟抖动.数据包丢失率信息的控制台命令程序,通过参数选项可以方便地看出,通过设置不同的选项值对网络带宽的影响,对于学习网络编程还是有一定的借鉴意义,至少 ...
- JUC中Lock和ReentrantLock介绍及源码解析
Lock框架是jdk1.5新增的,作用和synchronized的作用一样,所以学习的时候可以和synchronized做对比.在这里先和synchronized做一下简单对比,然后分析下Lock接口 ...
- Android HandlerThread使用介绍以及源码解析
摘要: 版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.HandlerThread的介绍及使用举例 HandlerThread是什么鬼?其本质就是一个线程,但是Han ...
随机推荐
- Postman模拟浏览器网页请求并获取网页数据
本文介绍在浏览器中,获取网页中的某一个请求信息,并将其导入到Postman软件,并进行API请求测试的方法. Postman是一款流行的API开发和测试工具,它提供了一个用户友好的界面,用于创 ...
- JDK14的新特性:JFR,JMC和JFR事件流
目录 简介 JFR JMC 创建JFR 分析JFR JFR事件 JFR事件流 总结 JDK 14的新特性:JFR,JMC和JFR事件流 简介 Java Flight Recorder(JFR)是JVM ...
- OpenHarmony系统使用gdb调试init
前言 OpenAtom OpenHarmony(简称"OpenHarmony")适配新的开发板时,启动流程init大概率会出现问题,其为内核直接拉起的第一个用户态进程,问题定位 ...
- SQline安装
SQLite 安装 SQLite 的一个重要的特性是零配置的,这意味着不需要复杂的安装或管理.本章将讲解 Windows.Linux 和 Mac OS X 上的安装设置. 在 Windows 上安装 ...
- 一键部署openGauss2.0.1 CentOS 7.6
一键部署 openGauss2.0.1[CentOS 7.6] 本文档目的是为了帮助高校学生提供基于 CentOS7.6 操作系统,实现 openGauss 数据库一键式安装的脚本. 该脚本执行成功后 ...
- Avalonia下拉可搜索树(TreeComboBox)
1.需求分析 树形下拉的功能是ComboBox和TreeView的功能结合起来,再结合数据模板来实现这一功能. 2.代码实现 1.创建UserControl集成TreeView控件 2.将 ...
- 英语 one day
前言 I do not know if it work,but just go. 内容 1.quote vt:摘要,引用 n:引语 He quote a passage from the presid ...
- 重新整理.net core 计1400篇[一] (.net core 命令行)
前言 把.net core 从新整理一遍. 下面介绍命令行. 正文 运行一下:dotnet new --list 那么这个时候会返回非常多的模板给你们. 这时候会给我们列出:project Templ ...
- python实现快排算法,传统快排算法,数据结构
def quick_sort(lists,i,j): if i >= j: return list pivot = lists[i] low = i high = j while i < ...
- 【Oracle】力扣简单的练习题
Oracle力扣简单的练习题 请你编写一个 SQL 查询来交换所有的 'f' 和 'm' /* Write your PL/SQL query statement below */ /******** ...