Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

异步任务的串行控制 #251

Open
xxleyi opened this issue Sep 25, 2020 · 0 comments
Open

异步任务的串行控制 #251

xxleyi opened this issue Sep 25, 2020 · 0 comments
Labels

Comments

@xxleyi
Copy link
Owner

xxleyi commented Sep 25, 2020

看到一个有趣的面试题:sl1673495/blogs#55

据说是蚂蚁金服的面试题,实现起来确实有坑,在参考了已有的两个解法之后,我理清了题意,找到了或许是最简易的一种解法。

题目:

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 的执行只需要支持串行。

分析:题目的本质问题是,扔过来一堆嵌套的 effects,实现 run 方法,将这些 effects 以异步串行的方式执行完毕。同时,run 方法会接受单个 effects 或者回调函数,flow 执行完毕之后开始执行。

那我们就要想想,JS 中甚至抛开具体语言来说,异步串行的编程方案是什么?

  • callback 回调,但在嵌套情况下会形成回调地狱
  • promise then,但在嵌套情况下会形成 then 递归
  • async await,如何解决嵌套问题?生成器

或许是最佳的这个解法恰好使用了 JS 中最新的编程能力:async await + 生成器

其中:生成器可以解决嵌套问题,async await 完美契合逻辑上同步,执行上异步异步串行

代码:

function Flow(flow) {
  this.flow = flow
}

async function _run(effects) {
  for (const effect of effects) {
    await effect()
  }
}

const genEffects = function* (flow) {
  for (const e of flow) {
    if (Array.isArray(e)) {
      yield* genEffects(e)
    } else if (e instanceof Flow) {
      yield* genEffects(e.flow)
    } else {
      yield e
    }
  }
}

Flow.prototype.run = function (done) {
  _run(genEffects(this.flow)).then(
    () => {
      if (typeof done === 'function') done()
    }
  )
}

function createFlow(flow) {
  return new Flow(flow)
}


const log = console.log
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 的顺序打印
@xxleyi xxleyi added the HOW label Sep 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant