承诺始终解析为 null,尽管在异步函数中返回了一个值



我对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

最新更新