我试图避免异步函数的并发性,以等待该函数的第一次调用完成,然后才能再次调用同一个函数:

const disallowConcurrency = (fn) => {
  let inprogressPromise = Promise.resolve();

  return async(...args) => {
    await inprogressPromise;
    inprogressPromise = inprogressPromise.then(() => fn(...args));
  };
};

const initCmp = async(arg) => {
  return new Promise((res) => {
    console.log('executed');
    setTimeout(() => res(console.log(arg)), 1000);
  });
};
const cmpConcurrentFunction = disallowConcurrency(initCmp);
cmpConcurrentFunction('I am called 1 second later');
cmpConcurrentFunction('I am called 2 seconds later');

所以我在这里创建了一个闭包,promise 作为传递给内部函数的值.

但我不明白,如果我删除了await inprogressPromise,为什么会继续工作,例如:

    const disallowConcurrency = (fn) => {
  let inprogressPromise = Promise.resolve();

  return async (...args) => {
    inprogressPromise = inprogressPromise.then(() => fn(...args));
  };
};

所以await不是必须的?为什么?

此外,我还期待这一点能够奏效:

 const disallowConcurrency = (fn) => {
  let inprogressPromise = Promise.resolve();

  return async (...args) => {
    await inprogressPromise;
    inprogressPromise = fn(...args);
  };
};

因为我在想,首先我在等待之前的promise 完成,然后我会打电话给你,并在inprogressPromise上分配返回的promise .

但它不起作用,这两个函数同时被调用.

有人能告诉我这是怎么回事吗?

如果你try 代码,你会看到第二次调用

cmpConcurrentFunction("I am called 2 seconds later")

将等待第一个promise 实现.

基本上,我已经编写了一个 node 包,通过npm导入到web应用程序的浏览器中.

但我的观点是试图理解为什么代码可以工作,如果我使用上一个版本,为什么它不能工作

谢谢

推荐答案

为了回答您的问题,让我先解释一下您的代码是如何工作的.

了解代码的工作原理

以下步骤解释了代码的执行:

  1. 脚本执行开始

  2. 调用disallowConcurrency函数,将initCmp作为参数传入.cmpConcurrentFunction被赋予disallowConcurrency函数的返回值

  3. 第一次打cmpConcurrentFunction,以'I am called 1 second later'作为参数传递.在调用过程中,inprogressPromisePromise.resolve()返回的已解决promise .等待它会暂停函数的执行.

  4. 第二次打cmpConcurrentFunction,以'I am called 2 seconds later'作为参数传递.在第二次调用期间,inprogressPromise仍然是Promise.resolve()返回的已解决promise .等待它会暂停函数的执行.

  5. 脚本的同步执行到此结束.事件循环现在可以开始处理微任务队列

  6. 由于第一次调用cmpConcurrentFunction而暂停的函数被恢复,根据Promise.resolve()返回的promise 调用then()方法.inprogressPromise的值通过向其分配inprogressPromise.then(...)返回的新promise 来更新

  7. 由于第二次调用cmpConcurrentFunction而暂停的函数被恢复.从第6步,我们知道inprogressPromise现在是inprogressPromise.then(...)返回的promise .因此,在上面调用then(),我们只是创建一个promise 链,在步骤6中创建的promise 链末尾添加一个新的then()方法调用.

    在这一点上,我们有一个链,看起来像这样:

    inProgressPromise
      .then(() => fn('I am called 1 second later'))
      .then(() => fn('I am called 2 seconds later')) 
    
  8. 调用前then个方法的回调函数,然后调用fn参数,即initCmp函数.呼叫initCmp设置一个计时器,该计时器解析promise initCmp在1秒后返回.当promise 在1秒后得到解决时,控制台上会记录'I am called 1 second later'.

  9. 当解析第一个then方法的回调函数中第一次调用initComp函数返回的promise 时,它解析第一个then方法返回的promise .这导致调用第二个then方法的回调函数.这将再次调用initComp函数,然后该函数返回一个新的promise ,并在1秒后解析.

这就解释了为什么2秒后你会看到'I am called 2 seconds later'个用户登录控制台.


现在回答你的问题:

但我不明白,如果我移除了等待,为什么它还能继续工作

await inprogressPromise除了暂停对cmpConcurrentFunction函数的调用之外,在代码中没有其他用途.两个电话都在等待Promise.resolve()返回的相同promise .

所以等待是没有必要的?为什么?

因为您在控制台上看到的输出不是因为await,而是因为promise 链(上面的步骤7)是由cmpConcurrentFunction函数的两次调用构成的.

此外,我还期待这一点能够奏效:

const disallowConcurrency = (fn) => {   
  let inprogressPromise = Promise.resolve();

  return async (...args) => {
    await inprogressPromise;
    inprogressPromise = fn(...args);   
  }; 
};

上面的代码不能像您的原始代码那样工作,因为现在没有构建promise 链,它是原始代码生成的输出的关键.

disallowConcurrency的这个实现中,代码的执行如下所述:

  1. 调用cmpConcurrentFunction函数,将'I am called 1 second later'作为参数传入

  2. 等待inprogressPromise会暂停函数的执行

  3. 第二次打cmpConcurrentFunction,以'I am called 2 seconds later'作为参数传递

  4. 等待inprogressPromise会暂停函数的执行. At this point, both invocations of cmpConcurrentFunction are paused and both function invocations are awaiting the same promise that was created as a result of calling Promise.resolve().

    这是这里的关键点:cmpConcurrentFunction函数的两个调用都在等待Promise.resolve()创建的samepromise .为什么两次祈祷都在等待同一个promise ?由于cmpConcurrentFunction是第二次调用before,因此cmpConcurrentFunction的第一次调用将恢复并调用initComp函数

  5. 继续第一次调用cmpConcurrentFunction,调用initComp函数并将其返回值分配给inprogressPromise.

    调用initComp函数会设置一个计时器,用于解析promise initComp函数在1秒后返回的值

  6. 继续第二次调用cmpConcurrentFunction,调用initComp函数并将其返回值指定给inprogressPromise,覆盖当前值inprogressPromiseinprogressPromise是第一次调用initComp函数返回的promise .

    调用initComp函数会设置一个计时器,用于解析promise initComp函数在1秒后返回的值

此时,initComp函数已被调用两次,设置了两个单独的计时器,每个计时器在1秒后解析各自的promise .

总结disallowConcurrency函数的原始实现与此实现之间的区别在于,在原始实现中,您有一个promise 链,它会按顺序解析promise ,而在第二个实现中,您有两个separatepromise (彼此不依赖),并且每个promise 都会在1秒后解析.

Javascript相关问答推荐

可以将SuperTest导入为ES 6模块吗?

为什么函数不使用以前函数返回的参数?

文件阅读器未读取一个文件

如何在我的Web应用程序中使用JavaScript显示和隐藏喜欢/不喜欢按钮?

Express.js:以块形式发送响应

在卡信息之间切换

调用SEARCH函数后,程序不会结束

如何通过在提交时工作的函数显示dom元素?

硬币兑换运行超时

将状态向下传递给映射的子元素

togglePopover()不打开但不关闭原生HTML popover'

在Three JS中看不到补间不透明度更改效果

制作钢琴模拟器,并且在控制台中不会执行或显示该脚本

Next.js服务器端组件请求,如何发送我的cookie token?

将异步回调转换为异步生成器模式

在SHINY R中的嵌套模块中,不能使用Java代码

如何在Java脚本中对列表中的特定元素进行排序?

JS Animate()方法未按预期工作

Jexl to LowerCase()和Replace()

Pevent触发material 用户界面数据网格中的自动保存