xxleyi / learning_list

聚集自己的学习笔记
11 stars 3 forks source link

字节面试题:修改 print 函数,使之正序或逆序打印 0-99 #256

Open xxleyi opened 3 years ago

xxleyi commented 3 years ago

要求:

1、只能修改 setTimeoutMath.floor(Math.random() * 1000 的代码

2、不能修改 Math.floor(Math.random() * 1000

3、不能使用全局变量

function print(n){
  setTimeout(() => {
    console.log(n);
  }, Math.floor(Math.random() * 1000));
}
for(var i = 0; i < 100; i++){
  print(i);
}

解:

看原帖中的解答,除了一个回答之外,都在利用 trick 绕过随机延迟,但也有人疑惑:题目本意应该不是如此。有一位大神也给出了一个保留原来随机延迟的解答。但写法太过妖娆,看不大明白。

在昨晚看到这个题之后,百思不解之下,我就睡觉了。今早睡醒,躺在床上想了想,突然就悟了:这就是要把异步并发改为异步串行啊!

那就队列走起,Promise 走起,布尔控制变量走起呗?

什么?不允许使用全局变量?那就函数属性走起呗,反正函数也是对象,大不了干完活我再删除相关属性。我悄悄的来,悄悄的走。

基本思路:for 循环入队异步任务,入队结束后,触发异步任务顺序执行。

代码如下:

// 为了调试方便,改成 10 个数,随机时间小于 100ms
function print(n, reverse = false) {
  setTimeout((t) => {
    print.times = print.times || []
    print.delay = print.delay || []
    print.trigger = print.trigger || false
    print.times.push(t)
    print.delay.push(() => {
      // 加个 reverse 参数控制异步队列执行顺序,虽然表面上违背了题目要求,但精神上应该没有,大不了硬编码
      const next = n + (reverse ? -1 : 1)
      new Promise(resolve => {
        console.log("delay", print.times[n])
        setTimeout(() => {
          console.log(n)
          resolve()
        }, print.times[n])
    }).then(() => {
        if (print.delay[next]) {
          print.delay[next]()
        } else {
          delete print.times
          delete print.delay
          delete print.trigger
        }
      })
    })
    if (print.trigger) { } else {
      // 所有异步任务都入队之后,再触发异步队列的执行,从头到尾,或从尾到头
      setTimeout(() => print.delay[reverse ? print.delay.length - 1 : 0]())
      // 保证只 trigger 一次
      print.trigger = true
    }
  }, 0, Math.floor(Math.random() * 100));
}

for(var i = 0; i < 10; i++){
  print(i);
}

总结:这样能按照给定延迟时间在逻辑上顺次执行。但也有一个缺陷:如果罗列多个循环,会产生调度问题。但超出此题目范围,只能根据具体问题来解决了,说不定真实需求下,就是只用一次呢?哈哈哈。