异步函数从不 returns

Async function never returns

我正在使用 Node 版本 7.6.0 来试用本机异步和等待功能。

我想弄清楚为什么我的异步调用只是挂起而从未真正解决。

自然语言处理模块:

const rest = require('unirest')
const Redis = require('ioredis')
const redis = new Redis()
const Promise = require('bluebird')
const nlp = {}
nlp.queryCache = function(text) {
    return new Promise(function(resolve, reject) {
        redis.get(text, (err, result) => {
            if (err) {
                console.log("Error querying Redis: ", err)
                reject(new Error("Error querying Redis: ", err))
            } else {
                if (result) {
                    let cache = JSON.parse(result)
                    console.log("Found cache in Redis: ", cache)
                    resolve(cache)
                } else {
                    resolve(null)
                }
            }
        })
    })
}

nlp.queryService = function(text) {
    console.log("Querying NLP Service...")
    return new Promise(function(resolve, reject) {
        rest.get('http://localhost:9119?q=' + text)
            .end((response) => {
                redis.set(text, JSON.stringify(text))
                resolve(response.body)
            })
    })
}

nlp.query = async function(text) {
    try {
        console.log("LET'S TRY REDIS FIRST")
        let cache = await nlp.queryCache(text)
        if (cache) {
            return cache
        } else {
            let result = await nlp.queryService(text)
            console.log("Done Querying NLP service: ", result)
            return result
        }
    } catch (e) {
        console.log("Problem querying: ", e)
    }

}
module.exports = nlp

模块消费者:

const modeMenu = require('../ui/service_mode')
const nlp = require('../nlp')
const sess = require('../session')
const onGreetings = async function(req, res, next) {
    let state = sess.getState(req.from.id)
    if (state === 'GREET') {        
        let log = { 
            middleware: "onGreetings"           
        }
        console.log(log)
        let result = await nlp.query(req.text)
        console.log("XXXXXXXX: ", result)
        res.send({reply_id: req.from.id, message: msg})

    } else {
        console.log("This query is not not normal text from user, calling next()")
        next()
    }
};
module.exports = onGreetings;

我无法获取继续执行以下行的代码:

console.log("XXXXXXXX: ", result) 

在NLP模块中可以看到查询成功

Edit: Added console.log statement to response body

最可能的原因是您没有捕捉到 Promise 中的错误。我发现它有助于避免 try-catch 除了顶部调用方法之外的所有方法,如果一个方法可以 await-ed 它几乎总是应该是。

在你的情况下,我认为问题出在这里:

nlp.queryService = function(text) {
    console.log("Querying NLP Service...")
    return new Promise(function(resolve, reject) {
        rest.get('http://localhost:9119?q=' + text)
            .end((response) => {
                redis.set(text, JSON.stringify(text)) // this line is fire and forget
                resolve(response.body)
            })
    })
}

特别是这一行:redis.set(text, JSON.stringify(text)) - 该行正在调用一个函数,没有发现任何错误。

解决方法是将所有 Redis 方法包装在 promises 中,然后始终 await 它们:

nlp.setCache = function(key, value) {
    return new Promise(function(resolve, reject) {
        redis.set(key, value, (err, result) => {
            if (err) {
                reject(new Error("Error saving to Redis: ", err));
            } else {
                resolve(result);
            }
        });
    })
}

nlp.queryService = async function(text) {
    console.log("Querying NLP Service...")
    const p = new Promise(function(resolve, reject) {
        rest.get('http://localhost:9119?q=' + text)
            .end((response) => { resolve(response.body) });

        // This is missing error handling - it should reject(new Error... 
        // for any connection errors or any non-20x response status 
    });

    const result = await p;

    // Now any issue saving to Redis will be passed to any try-catch
    await nlp.setCache(text, result);
    return;
}

作为一般规则,我发现最佳做法是:

  • 保持低水平的显式承诺 - 为您的 restredis 回调提供 Promise 包装函数。
  • 确保您在出现问题时 rejectnew Error 的承诺。如果 Promise 没有 resolve 也没有 reject 那么你的代码就到此为止了。
  • 对这些承诺包装器之一的每次调用都应该有 await
  • try-catch 就在顶部 - 只要每个 Promise 都被 await 编辑,它们中的任何一个抛出的任何错误都会在顶级 catch

大多数问题是:

  • 您有一个 Promise 可能无法 resolvereject
  • 您在没有 await 的情况下调用 async functionPromise