Предотвратите одновременные ajax запросы, заставьте их ждать - PullRequest
0 голосов
/ 02 мая 2020

Моему клиентскому приложению Javascript требуется определенная структура данных со стороны сервера ("списки"). Несколько разных мест в приложении требуют доступа к спискам. При запуске все эти места делают запрос, что приводит к нескольким одновременным запросам к серверу для одной и той же структуры данных.

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

Я думаю, что метод заключается в создании массива или стека обещаний. Если первый запрос выполняется, то каждый последующий запрос создает обещание, которое создается и добавляется в стек. Когда ответ возвращается, система проходит через стек и разрешает все обещания с результатом.

Проблема в том, что я не могу понять правильный синтаксис, чтобы заставить это работать. Это то, что у меня пока есть:

let lists = [];
let loading = false;
let promises = [];

function getLists() {
  if (lists.length > 0) {
    // return cached copy
    return Promise.resolve(lists);
  }

  /*
  This method can get called several times in quick succession on startup.
  To prevent sending multiple requests to the server, we maintain 
  a stack of promises. If there is already a request in-flight, then just
  add a promise to the stack and return it. Then resolve all promises
  in the stack when the request returns.
  */
  let prom = new Promise();  // BAD, NOT ALLOWED
  promises.push(prom);
  if (!loading) {
    callListsApi(); // async call, resolves promises
  }
  return prom;
}

function callListsApi() {
  loading = true;
  axios.get("/lists").then(
    response => {
      loading = false;
      if (!response.data || response.data.length == 0) {
        lists = [];
      } else {
        lists = response.data;
      }
      for (let i = 0; i < promises.length; i++) {
        promises[i].resolve(lists); // give all the callers their lists
      }
      promises = [];
    },
    error => {
      loading = false;
      util.handleAxiosError(error);
      let msg = util.getAxiosErrorText(error);
      for (let i = 0; i < promises.length; i++) {
        promises[i].reject(msg);
      }
      promises = [];
    }
  );
}

Это не работает, потому что вы не можете создать голый Promise (), не вставив в него какую-то функцию-исполнитель.

Как мне переписать это, чтобы оно работало?

1 Ответ

0 голосов
/ 02 мая 2020

Разобрался. Ответ не в том, чтобы создать стек обещаний. Просто возвращать каждому запрашивающему одно и то же обещание снова и снова. Мы придерживаемся одного основного обещания, которое существует только тогда, когда запрос находится в полете. Это намного проще.

let lists = [];
let listPromise = null;

function getLists() {
  if (lists.length > 0) {
    // return cached copy
    return Promise.resolve(lists);
  }

  if (listPromise != null) {
    // if we get here, then there is already a request in-flight
    return listPromise;
  }

  listPromise = callListsApi();
  return listPromise;
}

function callListsApi() {
  return axios.get("/lists").then(
    response => {
      if (!response.data || response.data.length == 0) {
        lists = [];
      } else {
        lists = response.data;
      }
      listPromise = null;
      return lists;
    },
    error => {
      util.handleAxiosError(error);
      listPromise = null;
      return util.getAxiosErrorText(error);
    }
  );
}
...