Skip to content

1. Array

1.1. 对象数组去重

点击查看
ts
interface Item {
  name?: string
  age?: number
  id: number
}
const arr: Item[] = [
  { name: '给我一个div', age: 10, id: 1 },
  { name: 'yh', age: 12, id: 2 },
  { name: 'zhy', age: 6, id: 3 },
  { name: 'yh', age: 13, id: 2 },
  { name: '给我一个div', age: 15, id: 1 },
]
ts
const unique = (arr: Item[], key = 'id') => {
  const obj = {}
  return arr.reduce<Item[]>((acc, item) => {
    return (obj[item[key]] || (obj[item[key]] = true && acc.push(item)), acc)
  }, [])
}
ts
const unique = (arr: Item[], key = 'id') => {
  const map = new Map()
  return arr.reduce<Item[]>((acc, item) => {
    !map.has(item[key]) && acc.push(item)
    map.set(item[key], item)
    return acc
  }, [])
}

1.2. 多维数组扁平化


点击查看
ts
const flatten = (arr: any[]) => arr.flat(Number.POSITIVE_INFINITY)
ts
/**
 *
 * @param arr 要扁平化的数组
 * @param deep 扁平深度,默认扁平一层
 */
const flatten = (arr: any[], deep = 1) => {
  if (deep <= 0)
    return arr
  return arr.reduce<any[]>((acc, item) => {
    return acc.concat(Array.isArray(item) ? flatten(item, deep - 1) : item)
  }, [])
}
ts
/**
 *
 * @param arr 要扁平化的数组
 * @param deep 扁平深度,默认扁平一层
 */
function flatten(arr, deep = 1) {
  const stack = [...arr]
  const res: any[] = []
  let index = 0 // 记录出栈的索引
  while (deep-- > 0 && stack.length) {
    // 使用 shift 从 stack 中取出并移除值
    const next = stack.shift()
    /**
     * 是数组 推回栈内  不是数组  在原索引位置插入元素
     */
    Array.isArray(next) ? stack.push(...next) : res.splice(index++, 0, next)
  }
  return [...stack, ...res]
}
ts
function flatten(arr) {
  while (arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr)
  }
  return arr
}

2. 异步串行

点击查看
ts
export type Tasks = (() => Promise<any>)[]

const tasks = [
  () => new Promise(resolve => setTimeout(() => {
    console.log('1', 1)
    resolve(1)
  }, 1000)),
  () => new Promise(resolve => setTimeout(() => {
    console.log('2', 2)
    resolve(2)
  }, 1000)),
  () => new Promise(resolve => setTimeout(() => {
    console.log('3', 3)
    resolve(3)
  }, 1000))
]
ts
import type { Tasks } from './tasks'

function asyncSerial(tasks: Tasks) {
  return tasks.reduce<Promise<any>>((p, task) => p.then(task), Promise.resolve())
}
ts
import type { Tasks } from './tasks'

async function asyncSerial(tasks: Tasks) {
  const results = []
  // 不能使用forEach forEach不支持异步等待
  for (const task of tasks) { 
    results.push(await task())
  }
  return results
}

3. 异步并行

点击查看
ts
export type Tasks = (() => Promise<any>)[]

export const tasks = [
  () => new Promise(resolve => setTimeout(() => {
    console.log('1', 1)
    resolve(1)
  }, 1000)),
  () => new Promise(resolve => setTimeout(() => {
    console.log('2', 2)
    resolve(2)
  }, 1000)),
  () => new Promise(resolve => setTimeout(() => {
    console.log('3', 3)
    resolve(3)
  }, 1000))
]
ts
import { type Tasks, tasks } from './tasks'

function asyncParallel(tasks: Tasks) {
  return Promise.all(tasks.map(task => task()))
}

asyncParallel(tasks).then((res) => {
  console.log('res', res) // 输出可能为: [1, 2, 3],但顺序可能不同,取决于任务完成的时间
})
ts
import { type Tasks, tasks } from './tasks'

async function asyncParallel(tasks) {
  const results = await Promise.all(tasks.map(task => task()))
  return results
}

asyncParallel(tasks).then((res) => {
  console.log('res', res) // 输出可能为: [1, 2, 3],但顺序可能不同,取决于任务完成的时间
})

4. 异步并发控制 async-pool

查看代码
ts
/**
 * @param poolLimit 需要同时发送请求的数量
 * @param iterable 一个数组,可以是url,也可以是每个请求得其他参数,数组的每一项会传递iteratorFn
 * @param iteratorFn 对数组的每一项做操作的函数,异步函数,真正发请求的函数
 * @returns 全部请求完成之后的结果
 */
async function asyncPool(poolLimit, iterable, iteratorFn) {
  const ret = [] // 保存所有异步任务
  const executing = new Set() // 保存正在执行的任务

  for (const item of iterable) {
    /**
     * 包装成promise,防止传入的不是异步函数
     * iteratorFn(item, iterable),iterable参数如果不需要页可以不用传递过去
     */
    const p = Promise.resolve().then(() => iteratorFn(item, iterable))
    // 添加任务到任务池 , 此时被添加的任务还没有执行,Promise状态为pending
    ret.push(p)
    executing.add(p)
    // 清理函数,正在执行的任务池中有完成的任务就清理出去
    const clean = () => executing.delete(p)
    // 当前的任务Promise执行完成之后(不管已经成功或者失败),从正在执行的任务池中清理出去
    p.then(clean).catch(clean)
    /**
     * 并发控制核心: 如果任务池中的任务数量大于等于限制,就等待其中一个任务完成,
     * 此时循环代码会跳出,等待Promise状态变为fulfilled,然后继续执行下一个循环,
     * 感觉这里应该用Promise.any比较合理
     */
    if (executing.size >= poolLimit)
      await Promise.race(executing)
  }
  // 返回所有的结果Promise  感觉这里应该用Promise.allSettled
  return Promise.all(ret)
}
ts
async function* asyncPool(concurrency, iterable, iteratorFn) {
  const executing = new Set()
  async function consume() {
    const [promise, value] = await Promise.race(executing)
    executing.delete(promise)
    return value
  }
  for (const item of iterable) {
    const promise = (async () => await iteratorFn(item, iterable))().then(
      value => [promise, value]
    )
    executing.add(promise)
    if (executing.size >= concurrency)
      yield await consume()
  }
  while (executing.size)
    yield await consume()
}
ts
import asyncPool from './asyncPool'

// 模拟请求函数
function getAsyncValue(i) {
  return new Promise((resolve) => {
    // 这里同时打印的数量不超过asyncPool poolLimit参数
    console.log('i', i)
    setTimeout(() => {
      resolve(i)
    }, 1000)
  })
}
async function handle() {
  // 每次同时发2个请求
  const result = await asyncPool(2, [1, 2, 3, 4, 5, 6, 7, 8, 9], getAsyncValue)
  console.log('result', result)
}
handle()

5. 请求重试

点击查看
ts
async function foo(callback: Promise<any>, limit = 3) {
  // const promise = Promise.resolve().then(() => callBack())
  for (let i = 0; i <= limit; i++) {
    try {
      // callBack成功 退出函数跳出循环
      return await callBack()
      // return await promise;
    }
    catch (err) {
      // callBack失败 继续下一次循环
      console.log('err', err)
      // 到达限定重试次数
      if (i === limit)
        throw err
    }
  }
}