# 02 Code

## 001 用递归算法实现，数组长度为 5 且元素的随机数在 2-32 间不重复的值

核心是生成随机数算法，`Math.random()` 函数返回一个随机浮点数，浮点数范围为左闭右开区间 `[0, 1)`。

```javascript
function rand(arr = [], length = 5, min = 2, max = 32) {
  if (arr.length < length) {
    const num = Math.floor(Math.random() * (max - min + 1)) + min
    if (!arr.includes(num)) {
      arr.push(num)
    }
    return rand(arr)
  }
  return arr
}
```

## 002 去除字符串中的空格，根据传入不同的类型分别能去掉前、后、前后、中间的空格

算法难点：

1. 通过 `Symbol()` 来生成枚举类型
2. 正则分组去除中间空格

```javascript
const POSITION = Object.freeze({
  left: Symbol(),
  right: Symbol(),
  both: Symbol(),
  center: Symbol(),
  all: Symbol(),
})

function trim(str, position = POSITION.both) {
  switch (position) {
    case POSITION.left:
      str = str.replace(/^\s+/, '')
      break
    case POSITION.right:
      str = str.replace(/\s+$/, '')
      break
    case POSITION.both:
      str = str.replace(/^\s+|\s+$/g, '')
      break
    case POSITION.center:
      while (str.match(/\w\s+\w/)) {
        str = str.replace(/(\w)(\s+)(\w)/, '$1$3')
      }
      break
    case POSITION.all:
      str = str.replace(/\s/g, '')
      break
    default:
      break
  }
  return str
}
```

## 003 去除字符串中最后一个指定的字符

```javascript
function delLast(str, char) {
  if (!str) return false
  if (!char) return str
  const index = str.lastIndexOf(char)
  return str.substring(0, index) + str.substring(index + 1)
}
```

## 004 设计实现 Promise.finally

```javascript
Promise.prototype.finally = function (callback) {
  let P = this.constructor // 获取当前实例构造函数的引用
  return this.then(
    (value) => P.resolve(callback()).then(() => value), // 接受状态：返回数据
    (reason) =>
      P.resolve(callback()).then(() => {
        throw reason
      }) // 拒绝状态：抛出错误
  )
}
```

## 005 设计实现 Promise.race

`Promise.race(iterable)` 方法返回一个 promise，一旦迭代器中的某个 promise 解决或拒绝，返回的 promise 就会解决或拒绝。

```javascript
Promise._race = (promises) =>
  new Promise((resolve, reject) => {
    promises.forEach((promise) => {
      promise.then(resolve, reject)
    })
  })
```

基本和上面的例子差不多，不同点是每个传入值使用 Promise.resolve 转为 Promise 对象，兼容非 Promise 对象。

```javascript
const _race = (p) => {
  return new Promise((resolve, reject) => {
    p.forEach((item) => {
      Promise.resolve(item).then(resolve, reject)
    })
  })
}
```

## 006 子字符串匹配算法

从长度为 n 的字符串 S 中，查找是否存在字符串 T，T 的长度是 m，若存在返回所在位置

```javascript
const find = (S, T) => {
  if (S.length < T.length) return -1
  for (let i = 0; i < S.length - T.length; i++) {
    if (S.substr(i, T.length) === T) return i
  }
  return -1
}
```

或者使用正则：

```javascript
// 方法一：
const find = (S, T) => S.search(T)

// 方法二：
const find = (S, T) => {
  const matched = S.match(T)
  return matched ? matched.index : -1
}
```

## 007 add(1)(2, 3) 链式调用函数

```javascript
add(1);  // 1
add(1)(2);  // 3
add(1)(2)(3)； // 6
add(1)(2, 3);  // 6
add(1, 2, 3);  // 6
```

```javascript
const add = (...rest) => {
  const args = [...rest]
  const fn = (...re) => {
    args.push(...re)
    return fn
  }

  fn.toString = () => {
    return args.reduce((a, b) => a + b)
  }

  return fn
}
```

## 008 如何快速从一个巨大的数组中随机获取部分元素

比如有个数组有 100K 个元素，从中不重复随机选取 10K 个元素。

```javascript
/* 洗牌算法：
  1.生成一个0 - arr.length 的随机数
  2.交换该随机数位置元素和数组的最后一个元素，并把该随机位置的元素放入结果数组
  3.生成一个0 - arr.length - 1 的随机数
  4.交换该随机数位置元素和数组的倒数第二个元素，并把该随机位置的元素放入结果数组
  依次类推，直至取完所需的10k个元素
*/

function shuffle(arr, size) {
  let result = []
  for (let i = 0; i < size; i++) {
    const randomIndex = Math.floor(Math.random() * (arr.length - i))
    const item = arr[randomIndex]
    result.push(item)
    arr[randomIndex] = arr[arr.length - 1 - i]
    arr[arr.length - 1 - i] = item
  }
  return result
}
```

## 009 实现一个批量请求函数 multiRequest(urls, maxNum)

要求如下：

1. 要求最大并发数 maxNum
2. 每当有一个请求返回，就留下一个空位，可以增加新的请求
3. 所有请求完成后，结果按照 urls 里面的顺序依次打出

```javascript
function multiRequest(urls, maxNum) {
  const ret = []
  let i = 0
  let resolve
  const promise = new Promise((r) => (resolve = r)) // resolve 赋值
  const addTask = () => {
    if (i >= urls.length) {
      // 全部任务结束
      return resolve()
    }

    const task = request(urls[i++]).finally(() => {
      addTask() // 如果有任务结束，则添加新任务
    })
    ret.push(task)
  }

  while (i < maxNum) {
    addTask() // 初始时添加任务至最大并发数
  }

  return promise.then(() => Promise.all(ret))
}

// 模拟请求
function request(url) {
  return new Promise((r) => {
    const time = Math.random() * 1000
    setTimeout(() => r(url), time)
  })
}
```

## 010 实现 Promise.retry，成功后 resolve 结果，失败后重试，尝试超过一定次数才真正的 reject

```javascript
Promise.retry = function (fn, times = 3) {
  return new Promise(async (resolve, reject) => {
    while (times--) {
      try {
        const ret = await fn()
        resolve(ret)
        break
      } catch (error) {
        if (!times) reject(error)
      }
    }
  })
}

function request() {
  return new Promise((resolve, reject) => {
    setTimeout(() => (Math.random() > 0.9 ? resolve(n) : reject(n)), 1000)
  })
}

Promise.retry(request)
```

## 011 自增 ID

```javascript
const genId = (() => {
  let count = 0
  return () => {
    return ++count
  }
})()
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://chanshiyu.gitbook.io/blog/qian-duan/note/02-code.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
