Как использовать Array.prototype.some () с асинхронной функцией? - PullRequest
2 голосов
/ 12 июня 2019

Я пытаюсь сделать следующее:

command.permissions.some(async permissionsKey => {
        switch (permissionsKey) {
            case "all": {
                return true;
            }
            case "OWNER": {
                return await msg.roomContext.isRoomOwnerId(msg.getStaticUserUID());
            }
            default: {
                return config.users_groups[permissionsKey].includes(msg.getStaticUserUID());
            }
        }
    });

Однако это всегда так, потому что Array.prototype.some не ожидает асинхронную функцию, поэтому при вызове функции возвращается обещание. Обещание правдиво.

Мне было интересно, как лучше всего использовать асинхронную функцию с любой из функций Array.prototype, в частности с функцией some.

Ответы [ 2 ]

3 голосов
/ 12 июня 2019

Если вы хотите получить результат, как только первое обещание разрешится с помощью true, вы также можете сделать это:

const somePromise = promises =>
    new Promise((resolve, reject) => {
        let resolveCount = 0;
        const resolved = value => {
            if (value) {
                resolve(true);
            } else if (++resolveCount === promises.length) {
                resolve(false);
            }
        };

        for (const promise of promises) {
            promise.then(resolved, reject);
        }
    });

Альтернативный причудливый подход:

const never = new Promise(() => {});

const somePromise = promises => Promise.race([
    Promise.race(promises.map(async p => !!await p || never)),
    Promise.all(promises).then(r => r.some(Boolean)),
]);

В вашемоднако, в конкретном случае, поскольку существует не более одного обещания, есть гораздо лучший способ сделать это:

let hasPermission =
    command.permissions.some(permissionsKey => {
        switch (permissionsKey) {
            case "all":
                return true;
            case "OWNER":
                return false;
            default:
                return config.users_groups[permissionsKey].includes(msg.getStaticUserUID());
        }
    });

if (!hasPermission && command.permissions.includes("OWNER")) {
    hasPermission = await msg.roomContext.isRoomOwnerId(msg.getStaticUserUID());
}
2 голосов
/ 12 июня 2019

Сначала преобразуйте массив в массив Promises, затем вызовите для него Promise.all и проверьте, верны ли .some массива результатов. Вы можете избежать ожидания разрешения всего массива Обещаний, проверив, действительно ли .some элементов в массиве Promise не являются Обещаниями, если вы хотите:

cpermisPromises = command.permissions.map(permissionsKey => {
  switch (permissionsKey) {
    case "all":
      {
        return true;
      }
    case "OWNER":
      {
        return msg.roomContext.isRoomOwnerId(msg.getStaticUserUID());
      }
    default:
      {
        return config.users_groups[permissionsKey].includes(msg.getStaticUserUID());
      }
  }
});
if (cpermisPromises.some(result => result && typeof result.then !== 'function')) {
  // at least one was truthy, don't need to wait for async call to complete
} else {
  Promise.all(cpermisPromises).then((results) => {
    if (results.some(result => result)) {
      // at least one was truthy
    }
  });
}

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

Вот альтернативный вариант, который позволяет проверить правильность любого из синхронных значений перед выполнением Обещаний:

let found = false;
const promFns = [];
forloop:
for (let i = 0; i < command.permissions.length; i++) {
  const permissionsKey = command.permissions[i];
  switch (permissionsKey) {
    case "all":
      found = true;
      break forloop;
    case "OWNER":
      proms.push(() => msg.roomContext.isRoomOwnerId(msg.getStaticUserUID()));
      break;
    default:
      if (config.users_groups[permissionsKey].includes(msg.getStaticUserUID())) {
        found = true;
        break forloop;
      }
  }
}
if (found) {
  // done, at least one truthy value was found synchronously
} else {
  // need to run promises
  Promise.all(
    promFns.map(fn => fn())
  )
    .then((results) => {
      if (results.some(result => result)) {
        // done, at least one truthy value was found asynchronously
      } else {
        // no truthy value was found
      }
    });
}

Или, если вы хотите отправлять запросы последовательно, что может привести к меньшему количеству запросов в целом, но займет больше времени, замените Promise.all на:

let foundProm = false;
for (const fn of promFns) {
  if (await fn()) {
    foundProm = true;
    break;
  }
}
if (foundProm) {
  // done, at least one truthy value was found synchronously
}else {
  // no truthy value was found
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...