问题
I am trying to do the following:
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());
}
}
});
However it always is true because Array.prototype.some does not expect an async function so a promise is returned when the function is called. A Promise is truthy.
I was wondering the best away to use an async function with any of the Array.prototype functions, specifically the some function.
回答1:
If you want the result as soon as the first promise resolves with true
, you can do that too:
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);
}
});
Alternative fancy approach:
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)),
]);
In your specific case, though, since there’s at most one promise, there’s a much better way to do it:
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:
Transform the array into an array of Promises first, then call Promise.all
on it, and check whether .some
of the results array are truthy. You can avoid waiting for the whole array of Promises to resolve by checking whether .some
of the items in the Promise array are truthy non-Promises, if you want:
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
can accept arrays containing any values, but will wait for all Promise values to resolve before the Promise.all
resolves.
Here's an alternative which allows you to check whether any of the synchronous values are truthy before running the 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
}
});
}
Or, if want to send out requests serially, which might lead to fewer requests overall, but will take longer to complete, replace the Promise.all
with:
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
}
来源:https://stackoverflow.com/questions/56553286/how-to-use-array-prototype-some-with-an-async-function