简体   繁体   中英

Promise - Resolving after multiple events

I am using a promise to asynchronously connect to an API. The API emits two events: connected and socketConnected .

As per my connect() function, I would like to wait for both of those events to fire.

connect() {
  return new Promise((resolve,reject)=>{
    this._client.on('connected', resolve);
    this._client.on('socketConnected', resolve);
    setTimeout(reject, 5000);
  }
}

However, you can only resolve once, and I want both events to fire before the promise resolves. How can I set this up so that it behaves that way?

You need to use Promise.all to create two promises, one for each event, and when both resolve - when the Promise.all resolves - you can resolve the promise that connect returns:

connect() {
  return new Promise((resolveAll, rejectAll) => {
    Promise.all([
      new Promise(res1 => this._client.on('connected', res1)),
      new Promise(res2 => this._client.on('socketConnected', res2))
    ]).then(resolveAll);
    setTimeout(rejectAll, 5000);
  });
}

You might also clear the timeout when the Promise.all resolves, if you want garbage collection to occur as soon as possible:

connect() {
  return new Promise((resolveAll, rejectAll) => {
    const rejectTimeout = setTimeout(rejectAll, 5000);
    Promise.all([
      new Promise(res1 => this._client.on('connected', res1)),
      new Promise(res2 => this._client.on('socketConnected', res2))
    ]).then(() => {
      clearTimeout(rejectTimeout);
      resolveAll();
    });
  });
}

I'm a fan of reusable code - if you find yourself wanting to timeout a Promise, then why not write a function to do so

const promiseTimeout = (promise, timeout) => {
    let timer;
    return Promise.race([
        promise, 
        new Promise((_, reject) => (timer = setTimeout(reject, timeout, promiseTimeout.symbol)))
    ])
    .then(result => (clearTimeout(timer), result));
};
promiseTimeout.symbol = Symbol('timeout');

Now, you can use that anywhere you need to set a time limit for a promise

In this case, as already observed, you'll need two promises for your logic, wrapped in a Promise.all, that get's you one promise that resolves when both promises resolve

ie

const promise = Promise.all([
    new Promise(resolve => this._client.on('connected', resolve)),
    new Promise(resolve => this._client.on('socketConnected', resolve))
]);

now, combining these two code snippets

const promiseTimeout = (promise, timeout) => {
    let timer;
    return Promise.race([
        promise, 
        new Promise((_, reject) => (timer = setTimeout(reject, timeout, promiseTimeout.symbol)))
    ])
    .then(result => (clearTimeout(timer), result));
};
promiseTimeout.symbol = Symbol('timeout');

function connect() {
    const promise = Promise.all([
        new Promise(resolve => this._client.on('connected', resolve)),
        new Promise(resolve => this._client.on('socketConnected', resolve))
    ]);
    return promiseTimeout(promise, 5000);
}

// if connect rejects with reason promiseTimeout.symbol, you can be sure it was because of the timeout

Though, the other answer is better in this case, I only offer this answer in case you do find yourself wanting to set a time limit on a promise resolution

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM