Promise 总是解析为 null,尽管在异步函数中返回了一个值
Promise always resolves to null, despite that a value is returned in async function
我的 async
函数有问题。
在内部 promise then
回调中,值设置正确,但返回此变量后,promise 解析,调用者总是得到一个空值!
注意:这是针对不和谐机器人的:我尝试使用他们的 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
语句,而且它 return 是 null
,所以它不可能 return 其他数据为了解决问题的承诺。
问题是您的数据 return 在 forEach
回调函数中编辑。但是 return 值将被遗忘。在 forEach
回调中返回数据是无用的。它没有任何用途。
你必须 return 所有承诺对象,你应该使用 .map
,而不是 .forEach
。然后使用 Promise.all
等待所有这些承诺解决。然后使用 .find
在那些不是 undefined
的值中找到第一个解析值。这是您希望 getUserInfo
承诺解决的值:所以 return that.
这是它的工作原理:
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
}
Trincot 的回答将 运行 针对所有用户的查询,但即使在找到第一个结果后,它也会等待 所有 查询完成后再解析结果.这就是他在回答中使用 Promise.all
的效果。
我们真正想要的是一个像 Array.prototype.find
一样工作但接受一系列承诺的函数。它应该 运行 每个 test
功能,但 resolve
一旦 第一个 value
通过测试。如果没有承诺值通过测试,则解析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
我的 async
函数有问题。
在内部 promise then
回调中,值设置正确,但返回此变量后,promise 解析,调用者总是得到一个空值!
注意:这是针对不和谐机器人的:我尝试使用他们的 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
语句,而且它 return 是 null
,所以它不可能 return 其他数据为了解决问题的承诺。
问题是您的数据 return 在 forEach
回调函数中编辑。但是 return 值将被遗忘。在 forEach
回调中返回数据是无用的。它没有任何用途。
你必须 return 所有承诺对象,你应该使用 .map
,而不是 .forEach
。然后使用 Promise.all
等待所有这些承诺解决。然后使用 .find
在那些不是 undefined
的值中找到第一个解析值。这是您希望 getUserInfo
承诺解决的值:所以 return that.
这是它的工作原理:
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
}
Trincot 的回答将 运行 针对所有用户的查询,但即使在找到第一个结果后,它也会等待 所有 查询完成后再解析结果.这就是他在回答中使用 Promise.all
的效果。
我们真正想要的是一个像 Array.prototype.find
一样工作但接受一系列承诺的函数。它应该 运行 每个 test
功能,但 resolve
一旦 第一个 value
通过测试。如果没有承诺值通过测试,则解析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