【问题标题】:How can multiple unconnected async events await a single promise多个未连接的异步事件如何等待一个承诺
【发布时间】:2020-05-26 07:37:35
【问题描述】:

我有一个系统,我需要来自服务器的 id 来处理事件。如果/当第一个事件发生时,我应该只获取 id,但在那之后,我需要为每个后续事件使用相同的 id。我知道如何使用 async-await 等,所以我有一些这样的代码

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
}

但我的问题是在收到响应之前我可能会收到多个事件,因此我会收到多个重叠调用来获取新的 id。

我怎样才能对handleEvent进行多个异步调用,第一个处理获取以及任何后续调用等待它完成以访问结果?

【问题讨论】:

  • 我认为这就像某种身份验证令牌?
  • 这是一个资源令牌,不是身份验证令牌,而是类似的想法。我只需要取一个。
  • 你可以用链式 .then 创建一个全局 Promise 来保证对 handleEvent 的累积调用的顺序,但你真的不应该这样做。你真的应该使用handleEvent返回的Promise。而你真正想要的是中止不是吗?

标签: javascript async-await


【解决方案1】:

创建一个函数以确保您只使用惰性承诺对 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() 以获取只会发生一次的 ID

import initialise from 'path/to/initialise'

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

  // do stuff with the ID
}

【讨论】:

  • 谢谢@FZs,我总是忘记那个
【解决方案2】:

当前接受的响应依赖于一个全局变量,这并不理想。另一种选择是使用类。

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

然后当您调用getId 时,您只需等待结果。如果之前没有请求,将发送网络请求。如果已经有一个待处理的请求,每个调用都将等待相同的结果。如果 promise 已经解析,你会立即得到结果。

const idManager = new IDManager();
async function handleEvent() {
   const id = await idManager.getId(URL);
   // Do stuff with the ID.
}

【讨论】:

  • OPs 代码中的var id 也是全局的,这取决于用例是否将其存储在数据结构中(例如,通过 url 键控)是更好的选择。单一的全局 idManager 不会有任何改进 :-)
  • @Bergi 我相信这是理性的人可能不同意的观点。 OP 可以通过将全局放入自己的模块来控制对全局的访问,我注意到答案现在反映了这一点。另一方面,这会降低您的灵活性。 IDManager 实例可以使用 getter/setter 控制对 ID 变量的访问,并且可以根据需要进行实例化。
【解决方案3】:

不清楚为什么你的函数被“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

【讨论】:

  • 该函数不只是获取 id。它正在处理一个事件“e”。它需要 id 来处理事件。
【解决方案4】:

这个问题的解决方案与之前的答案略有不同。我想我会发布我最终如何使它工作的。 @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 调用包装在另一个立即创建的 promise 中来避免这种情况,从而避免了重复。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-04
    • 2017-06-15
    • 1970-01-01
    • 2018-03-30
    • 1970-01-01
    • 1970-01-01
    • 2021-12-05
    相关资源
    最近更新 更多