【问题标题】:Promise always resolves to null, despite that a value is returned in async function尽管在异步函数中返回了一个值,但 Promise 始终解析为 null
【发布时间】:2021-05-26 01:05:16
【问题描述】:

async 函数有问题。

在内部promisethen回调中,值设置正确,但是返回这个变量后,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

有什么问题?

【问题讨论】:

  • 您的函数执行forEach,然后执行return null;。你还期待什么?这是它拥有的单个 return 语句。
  • 但是foreach中有一个返回...没有?
  • 是的,但这是 callback 函数的(无用)返回值,而不是您的外部函数。

标签: typescript promise


【解决方案1】:

你的函数getUserInfo只有一个return语句,它返回null,所以它不可能返回其他数据来解决promise。

问题是您的数据在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
}

【讨论】:

  • 我明白了,我来学习一下.map函数!非常感谢!
  • 这个答案很好解释,但在找到第一个匹配项时不会短路。请参阅我的答案以了解另一种方法:D
  • 是的,@ThankYou,我也想到了捷径——取决于第 3 方 API,这可能会节省一些时间。我为我的答案添加了一个不需要new Promise的替代捷径解决方案。
  • 很好地适应了Promise.any,但请注意,此解决方案确实吞下了fetch 中可能出现的其他自然错误。
【解决方案2】:

Trincot 的答案将对所有用户运行查询,但即使在找到第一个结果之后,它也会等待 所有 查询完成后再解析结果。这就是在他的回答中使用Promise.all 的效果。

我们真正想要的是一个像Array.prototype.find 一样工作但接受一系列承诺的函数。只要 first value 通过测试,它应该在每个人上运行 test 函数,但 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-18
    • 1970-01-01
    • 1970-01-01
    • 2018-08-12
    • 2023-03-13
    • 1970-01-01
    相关资源
    最近更新 更多