Как несколько несвязанных событий asyn c могут ждать одного обещания - PullRequest
4 голосов
/ 26 мая 2020

У меня есть система, в которой мне нужен идентификатор сервера для обработки событий. Мне нужно получить идентификатор только в том случае, если / когда произойдет первое событие, но после этого мне нужно использовать тот же идентификатор для каждого последующего события. Я знаю, как использовать asyn c -await et c. поэтому у меня есть такой код

var id = "";
async function handleEvent(e) {
    if (! id ) {
        let response = await fetch(URL)
        if (response.ok) { 
            let json = await response.json();
            id = json.id ;
        }
    }
    // use id to handle event
}

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

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

Ответы [ 4 ]

2 голосов
/ 26 мая 2020

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

const URL = 'whatever'
let idPromise // so lazy ?

const getId = async () => {
  const response = await fetch(URL)
  if (!response.ok) {
    throw response
  }
  return (await response.json()).id
}

const initialise = () {
  if (!idPromise) {
    idPromise = getId()
  }
  return idPromise
}

// and assuming you're using a module system
export default initialise

Теперь все, что вам нужно сделать, это префикс любого другого вызова с помощью initialise() чтобы получить идентификатор, который произойдет только один раз

import initialise from 'path/to/initialise'

async function handleEvent(e) {
  const id = await initialise()

  // do stuff with the ID
}
1 голос
/ 26 мая 2020

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

class IDManager {
    getId(URL) {
        if (this.id) {
            return this.id;
        }
        this.id = fetch(URL)
        return this.id
    }
}

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

const idManager = new IDManager();
async function handleEvent() {
   const id = await idManager.getId(URL);
   // Do stuff with the ID.
}
0 голосов
/ 31 мая 2020

Решение этого оказалось немного отличным от предыдущих ответов. Я думал, что напишу, как я заставил это работать в конце. Ответы от @phil и @vaelin действительно помогли мне в этом разобраться.

Вот и было мое решение ...

class IDManager {
    async fetchID (resolve,reject ) {
        const response = await fetch( URL, { } ) ;
        const id = await response.json() ;
        resolve( id );
    }
    async getID() {
        if ( this.id === undefined ) {
            if ( this.promise === undefined ) {
                var self = this;
                this.promise = new Promise( this.fetchID ).then( function(id) { self.id = id;} );
            }
            await this.promise;
        }
        return this.id;
    }
}

Проблема заключалась в том, что в ожидании выборки вызов getID выполнял пару секунд. В течение этого времени часто было несколько вызовов getID, каждый из которых инициировал другую выборку. Я избежал этого, заключив вызовы fetch и response. json в другое обещание, которое было создано мгновенно, и, таким образом, избежал дублирования.

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

Непонятно, почему ваша функция параметризована буквой «e». Я бы написал попроще:

async function request(URL) {
       let response = fetch(URL)
        if (response.ok) { 
            let json = await response.json();
            return json.id;
        }
        return false;
}

Затем, если последовательность ваших вызовов имеет значение, напишите их один за другим. В противном случае вы можете использовать Promise.all (возможно, Promise.allSettled), чтобы запускать их все сразу. https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

...