如何将Array.prototype.some()与异步函数一起使用?

时间:2019-06-12 00:43:35

标签: javascript asynchronous async-await

我正在尝试执行以下操作:

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不期望异步函数,因此在调用该函数时会返回一个promise。一个承诺是真实的。

我想知道将异步函数与Array.prototype函数(特别是some函数)一起使用的最佳途径。

3 个答案:

答案 0 :(得分:3)

如果您希望在第一个承诺以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());
}

答案 1 :(得分:2)

首先将数组转换为Promises数组,然后在其上调用Promise.all,并检查结果数组的.some是否真实。如果愿意,您可以通过检查Promise数组中的.some是否为真非Promise来避免等待整个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可以接受包含 any 值的数组,但是将在Promise.all解析之前等待所有Promise值解析。

这是一个替代方法,它使您可以在运行Promises之前检查任何同步值是否真实:

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
}

答案 2 :(得分:1)

我来这里是为了寻找类似的用途,然后发现这篇很棒的文章介绍了这个手册 asyncSome 函数: https://advancedweb.hu/how-to-use-async-functions-with-array-some-and-every-in-javascript/

当谓词返回真值时,它也会中断循环:

const arr = [1, 2, 3];

const asyncSome = async (arr, predicate) => {
    for (let e of arr) {
        if (await predicate(e)) return true;
    }
    return false;
};

// Example of use for this function:
const res = await asyncSome(arr, async (i) => {
    console.log(`Checking ${i}`);
    await sleep(10);
    return i % 2 === 0;
});

应用于此用例,它只是:

asyncSome(command.permissions, 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());
            }
        }
    });

就像 OP 建议的方式一样简单。