【经典面试题系列】实现异步串行

题目

有人在面试蚂蚁金服时,遇到过这么一道面试题,要求现场手写实现方式:

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

const subFlow = createFlow([() => delay(1000).then(() => log("c"))]);

createFlow([
  () => log("a"),
  () => log("b"),
  subFlow,
  [() => delay(1000).then(() => log("d")), () => log("e")],
]).run(() => {
  console.log("done");
});

// 需要按照 a,b,延迟1秒,c,延迟1秒,d,e, done 的顺序打印

按照上面的测试用例,实现 createFlow

  • flow 是指一系列 effects 组成的逻辑片段。
  • flow 支持嵌套。
  • effects 的执行只需要支持串行。


解题思路

  1. createFlow 函数接受一个 array 类型的参数,返回一个对象,对象有 run 方法
  2. createFlow 函数的数组参数里,分3种情况,函数、另一个createFlow返回值、数组(值和createFlow参数一样)
  3. 先写个架子,实现一个类 Flow ,构造函数接受数组,有一个 run 方法

class Flow {
constructor(queue) {
     this.queue = queue;
}

    run(callback) {
    }

}

run 方法用于串行执行操作队列queue,都完成后,执行callback(如果有的话)。

异步+串行,可以想到基于 Promise 链,再加上数组,可以考虑直接用 reduce 来把数组转成 Promise 链

  1. 在run里,分别针对 函数、flow对象、数组 单独处理:
  • 如果是函数,放到then里执行
  • 如果是 flow 对象,等待 run 方法执行完
  • 如果是数组,new 一个新的 flow 对象,并且等待run方法执行


实现代码

class Flow {
  constructor(queue) {
    this.queue = queue;
  }

  run(callback) {
    let p = Promise.resolve();
    p = this.queue.reduce((prev, obj) => {
      if (typeof obj === "function") {
        prev = prev.then(obj);
      } else if (obj instanceof Flow) {
        prev = prev.then(() => {
          return obj.run();
        });
      } else if (Array.isArray(obj)) {
        const temp = new Flow(obj);
        prev = prev.then(() => {
          return temp.run();
        });
      }
      return prev;
    }, p);
    if (typeof callback === "function") {
      p = p.then(() => {
        return callback();
      });
    }
    return p;
  }
}

function createFlow(arr{
  return new Flow(arr);
}

function log(...args{
  console.log(new Date(), ...args);
}

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

const subFlow = createFlow([() => delay(1000).then(() => log("c"))]);

createFlow([
  () => log("a"),
  () => log("b"),
  subFlow,
  [() => delay(1000).then(() => log("d")), () => log("e")]
]).run(() => {
  console.log("done");
});


总结

这道面试题主要的目的是考察对于异步串行流的控制,巧妙的利用自身的递归设计来处理传入的参数也是一个 flow的情况,在编写题目的过程中展示你对 Promise 的熟练运用,一定会让面试官对你刮目相看的~

祝大家在大环境不好的情况下,都能拿到自己满意的 offer,加油。

最后

“前端面试题宝典”经历接近一年的迭代打磨,目前已经提供了小程序刷题、PC端访问(https://fe.ecool.fun/)。

截至2022年4月2日,已经录入前端常见面试题832题,想刷前端面试题的小伙伴千万不要错过。

我们在近期推出了简历指导、模拟面试等增值服务,有想了解的小伙伴们可以添加小助手微信(interview-fe)进行咨询哦~