logo
Published on

异步输出 16 进制数字

Authors

在网上冲浪时看见了一个问题,比较感兴趣,所以自己尝试写了一下。 这个是 原题目网址 与对应的 API

分析

​题目的要求通过调用 xss_rpc_call 的方式输出异步请求的 16 进制结果。

API 调用样例:

xss_rpc_call(1, function (res) {
  console.log(1, res)
})
// 0x1

(2025-04-18 更新)由于原 API 已经无法调用,现对 xss_rpc_call API 进行 mock

function xss_rpc_call(num, callback) {
  const DelayMaxMs = 6000
  const randomDelay = Math.floor(Math.random() * DelayMaxMs) // 生成随机延迟
  const hexadecimalNumber = num.toString(16)

  // 使用 setTimeout 实现随机延迟调用
  setTimeout(function () {
    callback && callback(hexadecimalNumber)
  }, randomDelay)
}

递归解

最简单的肯定是递归解法:

function recursiveFunc(i) {
  // 递归边界条件
  if (i > 31) {
    return
  }
  // 递归
  xss_rpc_call(i, function (res) {
    recursiveFunc(i + 1)
    console.log(i, res)
  })
}

尽快输出

但是肯定不是我们理想的解法,考虑到 API 返回的数据有一个随机延迟,所以保证输出顺序的同时,应当尽快输出:

// 初始化输出队列与输出函数
const sequence = new Array(31).fill(null)
const performOutput = initOutput()

function asapOutput() {
  // 循环调用 API,将结果存入输出队列
  for (let i = 0; i < sequence.length; i++) {
    xss_rpc_call(i, function (res) {
      sequence[i] = res
      performOutput(i)
    })
  }
  console.log(sequence)
}
function initOutput() {
  let nextOutputIndex = 0 // 下一个应该输出值的索引
  return function (currentIndex) {
    // 当前调用输出函数的元素索引大于下一个输出值的索引,不应输出,early return
    if (currentIndex > nextOutputIndex) {
      return
    }
    // 从下一个该输出的结果开始顺序输出
    for (let j = currentIndex; j < sequence.length; j++) {
      const element = sequence[j]
      // 如果为 null 说明当前请求还没返回 16 进制数字,则更新下一个该输出的元素索引并结束函数调用
      if (element === null) {
        nextOutputIndex = j
        return
      } else {
        console.log(j, element)
      }
    }
  }
}
asapOutput()

输出效果

写了个 Demo 来演示一下输出效果:

Request Sequence

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

Output Sequence

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

后记

心血来潮写好后,在网上一搜,发现 Vue 的作者尤雨溪当年也曾写过这个。感兴趣的伙伴也可以 在这里康康