NodeJS Async / Await не ожидает (возвращает ожидающее обещание)

Я пишу API, который получает прошлые транзакции в сети Stellar для пользователя, ищет соответствующих пользователей в моей базе данных и объединяет все это в JSON (для ответа на запрос - позже).

Проблема: поиск имени пользователя, соответствующего идентификатору учетной записи (поле "from"), является асинхронным методом с mongoose и возвращает данные только после сборки JSON.

Я пробовал использовать async / await, promises, .thens, но ничего не работает.

server.payments()
    .forAccount(accountId)
    .cursor('now')
    .order('desc')
    .limit(2)
    .call()
    .then(function (page) {
        var history = []
        for(var i = 0; i<page.records.length; i++){
            var obj = page.records[i]
            //Get the username corresponding to the key from my database
            //FIXME
            var username
            findUser(obj["from"]).then(result => {
                username = result
            })
            var payment = {
                "currency":obj["asset_code"],
                "from": obj["from"],
                "username":username,
                "amount": obj["amount"],
                "timestamp":obj["created_at"]
            }
            history.push(payment)
        }
        console.log(history)
        //console.log(JSON.stringify(history))
    })
    .catch((err) => {
        console.log(err)
    })

async function findUser(senderKey){
    var user = await mongoose.User.findOne({publicKey: senderKey})
    console.log(user.username)
    return user.username
}

Ожидаемый результат: findUser возвращает значение, платежная переменная использует его и регистрируется вместе.

Что происходит: findUser начинает искать данные, переменная платежа собирается (имя пользователя undefined) и регистрируется, findUser возвращает данные.

Вот журнал: (человек-паук - настоящее имя пользователя из моей базы данных)

[ { currency: 'MEUR',
    from: 'GACRQARPR2OMWRG6IH7HM5DYTA3FMM6UKA7NKS4BIJIADRIKFRPAIE7G',
    username: undefined,
    amount: '3.0000000',
    timestamp: '2019-05-07T13:37:04Z' },
  { currency: 'MEUR',
    from: 'GACRQARPR2OMWRG6IH7HM5DYTA3FMM6UKA7NKS4BIJIADRIKFRPAIE7G',
    username: undefined,
    amount: '2.0000000',
    timestamp: '2019-05-07T13:34:21Z' } ]
spiderman
spiderman

person Community    schedule 07.05.2019    source источник


Ответы (3)


Вы можете использовать новый цикл for of вместе с async await: -

server.payments()
    .forAccount(accountId)
    .cursor('now')
    .order('desc')
    .limit(2)
    .call()
    .then(async function (page) {
        var history = []

        for(const obj of page.records) {
          const username = await findUser(obj["from"]);
            var payment = {
                "currency":obj["asset_code"],
                "from": obj["from"],
                "username":username,
                "amount": obj["amount"],
                "timestamp":obj["created_at"]
            }
            history.push(payment)
        }

        console.log(history)
        //console.log(JSON.stringify(history))
    })
    .catch((err) => {
        console.log(err)
    })
person cEeNiKc    schedule 08.05.2019
comment
В качестве альтернативы bluebird отлично подходит для циклов async / await. - person Michael Ossig; 08.05.2019
comment
Всегда лучше использовать что-то родное, чем внешнюю библиотеку, которая будет медленнее, чем собственный код. - person cEeNiKc; 08.05.2019
comment
Раньше я пропустил объявление async в функции .then (), поэтому он не сработал, когда я попробовал с await finduser(). Работает, спасибо. - person ; 08.05.2019
comment
да, это забавно @cEeNiKc, последнее, что я помню, bluebird на самом деле быстрее и легче для обработки обещаний, чем V8, потому что его оптимизирован для обещаний - person Michael Ossig; 09.05.2019
comment
Ого, посмотрите на это, в 4 раза быстрее! softwareengineering.stackexchange.com/questions/278778/, и если вам нравятся числа по сравнению с ES7: softwareengineering.stackexchange.com/questions/331991/ - person Michael Ossig; 09.05.2019
comment
Приятель, мы здесь не занимаемся созданием новых обещаний. Мы уже получили обещание от мангуста, с которым нам нужно справиться. Benchmark использует новый синтаксис Promise, но мы этим не занимаемся. Пожалуйста, прочтите ссылки, которыми вы поделились, как следует. - person cEeNiKc; 09.05.2019
comment
И спасибо, что указали на это, узнали что-то новое сегодня. - person cEeNiKc; 09.05.2019
comment
@cEeNiKc не хочет ли он обрабатывать коллекцию, в которой каждый элемент ожидает результата обещания? Если вы прочитаете ссылки, которыми я поделился, обработка Promise с коллекциями остается чем-то, в чем bluebird превосходит собственный JS. - person Michael Ossig; 12.05.2019

Выделите рекомендуемый вариант, вы можете упростить это с помощью синтаксиса async/await для вашего случая.

Вы будете wait, пока сервер не ответит на page, затем каждый из obj в page.records (.map или _7 _... вызовет у вас некоторый беспорядок), вы wait для функции findUser вернете username, а затем займитесь своим делом.

try {
  const page = await server.payments()
    .forAccount(accountId)
    .cursor('now')
    .order('desc')
    .limit(2)
    .call()
  const history = [];
  for (const obj of page.records) {
    const username = await findUser(obj["from"]);
    const payment = {
      "currency": obj["asset_code"],
      "from": obj["from"],
      "username": username,
      "amount": obj["amount"],
      "timestamp": obj["created_at"]
    }
    history.push(payment)
  }
  console.log(history)
} catch (e) {
  console.log(e)
}

Напоминание: await выражение разрешено только в async функции.

person hoangdv    schedule 08.05.2019

Вам нужен условный оператор, который проверяет, завершилась ли функция, которую вы ожидаете.

async function findUser(senderKey){
    var user = await mongoose.User.findOne({publicKey: senderKey})
    if (user){
      console.log(user.username)
      return user.username
    }
}

person Michael Ossig    schedule 08.05.2019
comment
На самом деле Майкл не в этом проблема. Он просто не ждет выполнения обещания, прежде чем перейти к следующей итерации. - person cEeNiKc; 08.05.2019
comment
Да, мне это тоже кажется проблемой. Дело в том, что я не знаю, как на самом деле ждать обещания. - person ; 08.05.2019
comment
Пожалуйста, проверьте решение, которое я предоставил. Это прояснит некоторые вещи. - person cEeNiKc; 08.05.2019