Node JS并发处理编码面试题

Node JS Concurrency Handling Coding Interview Question

我参加了 NodeJS 编码面试。得到以下从不同浏览器异步执行的代码(假设)。如果 ID 更新相同但从不同地方(比如浏览器)调用,我们的解决方案需要锁定函数的执行。然后释放锁以供下一个请求执行。

这里没有对下面提到的代码进行任何更改。

async function update(id, data) {
    console.log(`start --> id:${id}, data:${data}`);
    await randomDelay(); //update is happening here
    console.log(`end --> id:${id}, data:${data}`);
}

//=============================================================================
//================= Don't change anything below ===============================
//=============================================================================

//---- update() is getting called from many places ----
update(1, "browser 1");
update(1, "browser 2");

//========================= Utility functions ===================================
//========================= Don't change any here================================

async function sleep(ms) {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(), ms);
    });
}

async function randomDelay() {
    const randomTime = Math.round(Math.random() * 1000);
    return sleep(randomTime);
}

这将给出如下输出。

start --> id:1, data:browser 1
start --> id:1, data:browser 2
end --> id:1, data:browser 1
end --> id:1, data:browser 2

预期的答案是

start --> id:1, data:browser 1
end --> id:1, data:browser 1
start --> id:1, data:browser 2
end --> id:1, data:browser 2

请注意代码中的注释“不要更改下面的任何内容”。可能的解决方案是什么?

此解决方案仅适用于 two 使用不同数据的连续调用,因此我正在努力扩展它,但现在,我希望能让您对如何实施它有一个很好的了解

const callStack = []
async function update(id, data) {
    const stackLen = callStack.length;
    let currentIndex;

    if (stackLen) {
        let currentCall = callStack[stackLen - 1];
        if (currentCall.start == true && currentCall.await == true) {
            setImmediate(() => update(id, data))
            return;
        }
        currentIndex = stackLen - 1;
        if (currentCall.args[0] == id && currentCall.args[1] !== data) {
            
            if (currentCall.await === true) {
                currentCall.start = true;
                update(id, data)
                return;
            }
        }
    } else {
        callStack.push({ args: [...arguments], await: true })
        currentIndex = 0;
    }
    console.log(`start --> id:${id}, data:${data}`);
    await randomDelay(); //update is happening here
    console.log(`end --> id:${id}, data:${data}`);
    callStack[currentIndex].await = false;
}

您可以使用由 ID 键控的 queues 的散列 table,这样只有具有相同 ID 运行 的作业才能连续执行,否则它们会同时 运行。

let hash = {};
class Queue {
  constructor() {
    this.isBusy = false;
    this.jobs = [];
  }

  push(jobFn) {
    return new Promise((resolve) => {
      this.jobs.push({
        jobFn,
        resolve
      });
      this.next();
    });
  }

  next() {
    if (this.isBusy || this.jobs.length === 0) return;
    this.isBusy = true;
    let currJob = this.jobs.shift();
    return currJob.jobFn().then((data) => {
      currJob.resolve(data);
      this.isBusy = false;
      this.next();
    });
  }
}

async function update(id, data) {
  const updateFn = async () => {
    console.log(`start --> id:${id}, data:${data}`);
    await randomDelay(); //update is happening here
    console.log(`end --> id:${id}, data:${data}`);
  };
  if (id in hash) {
    hash[id].push(updateFn);
  } else {
    hash[id] = new Queue(updateFn);
    hash[id].push(updateFn);
  }
}

//=============================================================================
//================= Don't change anything below ===============================
//=============================================================================

//---- update() is getting called from many places ----
update(1, "browser 1");
update(1, "browser 2");
update(2, "browser 1");
update(2, "browser 2");
update(1, "browser 3");
update(1, "browser 4");

//========================= Utility functions ===================================
//========================= Don't change any here================================

async function sleep(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(), ms);
  });
}

async function randomDelay() {
  const randomTime = Math.round(Math.random() * 1000);
  return sleep(randomTime);
}