我对async
函数有问题。
在内部 promisethen
回调中,值设置正确,但是在返回此变量并且 promise 解析后,调用方总是得到一个 null 值!
注意:这是针对不和谐机器人的:我尝试使用用户的 ID 获取用户的显示名称。
这是async
函数:
export async function getUserInfo(userNameLooking: string, guild: Guild): Promise<UserSettings | null> {
console.log("Looking for user", userNameLooking);
userList.forEach(user => {
console.log("Analyzing user ID", user);
let thanos = guild.client.users.fetch(user);
thanos.then(function (result1) {
console.log("... This ID user name is", result1.username);
if (result1.username.toLowerCase() == userNameLooking.toLowerCase()) {
console.log("... Match !");
console.log(cacheUser[user] );
return cacheUser[user] ;
}
else {
console.log("... Not match ...");
}
}, function (){console.log("ERROR : Can't find name of ID", user)});
})
return null;
}
调用上述函数的代码:
var user;
getUserInfo(args.userName, message.guild).then(function (result1) {
console.log("Caller Result :", result1); // <--- always null!
user = result1;
if (user == null) {
return message.channel.send("User is unknown");
}
const embed = new MessageEmbed();
embed.setTitle("NAME: " + user.userId);
});
控制台中的输出:
Looking for user totolitoto
Analyzing user ID 752614956918112386
... This ID user name is TotoLitoto
... Match !
{
_id: 60abd6dada6f9ad06fbfb9eb,
userId: '752614956918112386',
userName: 'TotoLitoto',
userLang: 'en'
}
Caller Result : null
问题出在哪里?
>你的函数getUserInfo
只有一个return
语句,它返回null
,所以它不可能返回其他数据来解析承诺。
问题是您的数据在forEach
回调函数中返回。但是这个返回值将被遗忘。在forEach
回调中返回数据是没有用的。它不用于任何事情。
您必须返回所有 promise 对象,您应该使用.map
而不是.forEach
。然后等待所有这些承诺解决,使用Promise.all
.然后使用.find
找到未undefined
的解析值中的第一个解析值。这是您希望getUserInfo
承诺解析的值:因此返回该值。
以下是它的工作原理:
export async function getUserInfo(userNameLooking: string, guild: Guild): Promise<UserSettings | null> {
let results = await Promise.all(userList.map(user => {
// ^^^^^^^^^^^ ^^^
let thanos = guild.client.users.fetch(user);
return thanos.then(function (result1) {
// ^^^^^^
if (result1.username.toLowerCase() == userNameLooking.toLowerCase()) {
return cacheUser[user];
}
});
});
// Find the first non-undefined result,
// ... assuming that `catchUser[user]` is a truthy value.
return results.find(Boolean);
}
注意:我删除了错误处理,所以只关注问题。
短切
为了避免在我们感兴趣的承诺已经解决时等待剩余的承诺,您可以使用Promise.any
:
// In this version the function does not really need to be declared `async`:
export function getUserInfo(userNameLooking: string, guild: Guild): Promise<UserSettings | null> {
return Promise.any(userList.map(user => {
// ^^^
let thanos = guild.client.users.fetch(user);
return thanos.then(function (result1) {
if (result1.username.toLowerCase() == userNameLooking.toLowerCase()) {
return cacheUser[user]; // <-- will resolve the `.any` promise
}
// Throwing will avoid resolving the `.any` promise
// ... unless this is the last one, and there was no match found
throw new Error("no match");
});
}).catch(() => null); // resolve to null when nothing matches
}
Trincat 的答案将对所有用户运行查询,但即使在找到第一个结果后,它也会等待所有查询完成,然后再解析结果。这就是使用Promise.all
他回答的方式的效果。
我们真正想要的是一个像Array.prototype.find
一样工作的函数,但接受一系列承诺。它应该在每个函数上运行test
函数,但在第一个value
通过测试后立即resolve
。如果没有承诺的值通过测试,请解决undefined
-
async function find(promises, test) {
return new Promise((resolve, reject) => {
for (const p of promises)
p.then(value => test(value) && resolve(value), reject)
Promise.all(promises).then(_ => resolve(undefined))
})
}
将find
编写为泛型函数很有用,因为它允许getUserInfo
只关注其专用问题。它还使我们能够在您的程序中需要find
其他地方重用,而无需编写复杂的代码 -
function getUserInfo(query, guild) {
return find
( userList.map(user => guild.client.users.fetch(user))
, result =>
result.username.toLowerCase() == query.toLowerCase()
)
.then(user => user && cacheUser[user])
}
让我们看看使用下面的演示程序find
的实际效果 -
async function find(promises, test) {
return new Promise((resolve, reject) => {
for (const p of promises)
p.then(value => test(value) && resolve(value), reject)
Promise.all(promises).then(_ => resolve(undefined))
})
}
const sleep = ms =>
new Promise(r => setTimeout(r, ms))
async function fetch(query) {
await sleep(Math.random() * 3000)
return query
}
const queries =
[ "Alice", "Bob", "Carla", "Dina" ]
find(queries.map(fetch), result => result.length > 4)
.then(console.log, console.error) // "Carla" or "Alice"
find(queries.map(fetch), result => result.length > 5)
.then(console.log, console.error) // undefined