Как использовать асинхронную блокировку в приложении nodejs для блокировки функции ресурса /get

Я пытался использовать модуль npm async-lock для блокировки выполнения ресурса /get.

import cors from "cors";
import express from "express";
import AsyncLock from "async-lock";
import _ from "lodash";

const app = express();
const port = process.env.port || 3001;

app.use(cors());
app.use(express.json());

let synchonizationKey: string = "";
let lock = new AsyncLock();

app.get("/pckg", (req, res) => {
  let id = _.uniqueId();
  console.log("--------------Request ", id, " arrived");

  lock.acquire(
    synchonizationKey,
    function () {
      console.log("--------------Request ", id, " lockAquired");

      setTimeout(function () {
        console.log("--------------Request ", id, " Done");
      }, 3000);
    },
    function (err, ret) {
      console.log("--------------Request ", id, " lockReleased");
      res.send({ id: id });
    },
    {}
  );
});

app.listen(port, () => console.log("Running on port#" + port));

Вывод: 4 запроса на получение, которые я запускал из браузера.

--------------Request  1  arrived
--------------Request  1  lockAquired
--------------Request  1  lockReleased
--------------Request  2  arrived
--------------Request  2  lockAquired
--------------Request  2  lockReleased
--------------Request  3  arrived
--------------Request  3  lockAquired
--------------Request  3  lockReleased
--------------Request  4  arrived
--------------Request  4  lockAquired
--------------Request  4  lockReleased
--------------Request  1  Done
--------------Request  2  Done
--------------Request  3  Done
--------------Request  4  Done

Я ожидал результата в тех же строках, что и в этом вопрос

Похоже, что блокировка снимается, как только выполняются все синхронные части обратного вызова.

Может кто-нибудь, пожалуйста, помогите мне с этим?


person Anurag Singh    schedule 04.07.2021    source источник
comment
Должна быть функция с именем done, next и т. д. вам нужно вызвать ее вручную в блоке кода setTimeout, если есть какая-либо ошибка, обычно вам нужно передать ее в эту функцию, например: next(error), в этом контексте, какой тип проблемы решает async-lock ? Вы делаете это сложным, вам не нужна библиотека обратного вызова сейчас... вместо этого используйте стандартный async/await   -  person Nur    schedule 04.07.2021
comment
Клиент может вызвать /pckg для запуска серии асинхронных вызовов. Эти асинхронные вызовы должны быть защищены в запросах /pckg get.   -  person Anurag Singh    schedule 04.07.2021
comment
@Nur Бывают ситуации, когда вам может понадобиться блокировка. Например, несколько клиентов читают и обновляют один и тот же файл, при этом второй клиент не должен начинать чтение файла, пока другой не закончит обновление. Конечно, каждый может сам написать такую ​​блокировку, используя async/await, но нет нужды изобретать велосипед. Да, в настоящее время, вероятно, может быть предпочтительнее подход на основе обещаний - он также поддерживается библиотекой. Но все же нет ничего плохого в использовании подхода, основанного на обратном вызове...   -  person derpirscher    schedule 04.07.2021


Ответы (1)


Если вы посмотрите на документы пакета async-lock, вы заметите, что функция acquire имеет следующую подпись

acquire(
  key,
  executeFunction,
  callbackFunction,
  options);

где сам executeFunction имеет подпись

function(done) {...}

а done — это обратный вызов, который используется для вызова callbackFunction после завершения работы вашего критического раздела и снятия блокировки. Вы пропускаете параметр done и, таким образом, не вызываете callbackFunction вручную. Таким образом, он вызывается (и, следовательно, блокировка снимается) после завершения executeFunction. И поскольку setTimeout в вашей исполнительной функции, конечно же, также асинхронный, он не будет задерживать выполнение функции.

Итак, правильный способ его использования заключается в следующем.

lock.acquire(
  synchonizationKey,
  function (done) {  // <----- new parameter for the function
    console.log("--------------Request ", id, " lockAquired");

    setTimeout(function () {
      console.log("--------------Request ", id, " Done");
      done();  // <---- call the callback, once the lock can be released 
    }, 3000);
  },
  function (err, ret) {
    console.log("--------------Request ", id, " lockReleased");
    res.send({ id: id });
  },
  {});

Это приводит к следующему выводу

--------------Request  1  arrived
--------------Request  1  lockAquired
--------------Request  2  arrived
--------------Request  3  arrived
--------------Request  4  arrived
--------------Request  1  Done
--------------Request  1  lockReleased
--------------Request  2  lockAquired
--------------Request  2  Done
--------------Request  2  lockReleased
--------------Request  3  lockAquired
--------------Request  3  Done
--------------Request  3  lockReleased
--------------Request  4  lockAquired
--------------Request  4  Done
--------------Request  4  lockReleased
person derpirscher    schedule 04.07.2021
comment
Большое спасибо за подробное объяснение. Теперь это работает. Плохо, что я недостаточно внимательно прочитал документы. - person Anurag Singh; 04.07.2021