Javascript 快车中的异步地狱

Javascript 快车中的异步地狱,javascript,node.js,express,Javascript,Node.js,Express,我有一个问题是由于使用了异步而导致的,但事情不同步(是的,我知道这很常见),它看起来是这样的: 首先,express路由器从url获取调用,然后调用Middleware函数 router.post('/', checkFunction, (req, res, next) => { res.render('somepage.ejs', { output: res.locals.output; }); }); 现在,在checkFunction(过去仅为回调函

我有一个问题是由于使用了异步而导致的,但事情不同步(是的,我知道这很常见),它看起来是这样的:

首先,express路由器从url获取调用,然后调用Middleware函数

router.post('/', checkFunction, (req, res, next) => {
    res.render('somepage.ejs', {
        output: res.locals.output;
    });
});
现在,在checkFunction(过去仅为回调函数)中,我现在调用另一个使用axios且必须是异步的函数

function checkFunction(req, res, next) {
    if ('check') secondFunction(req, res, next);
next()
}
最后一个函数如下所示,它检查来自用户页面的输入是否有空字符串、好的请求和坏的请求,并将输出返回给用户,不管是空的好的还是坏的。 在错误的日志上,它执行tail命令从日志中获取有关错误的数据

async function secondFunction(req, res, next) {
    const url = 'http://somepage.com';
    if ('some check on req') res.locals.output = 'nothing';
    else {
        try {
            const data = await axios.get(url);
            res.locals.output = 'good request';
        } catch (error) {
            exec(`tail -n 1 file.log`, (error, stdout, stderr) => {
                console.log('err:', error);
                console.log('stdout:', stdout);
                console.log('stderr:', stderr);
                res.locals.output = stdout;
            });
        }
    }
}
问题是(正如预期的那样)第二个函数在axios请求完成之前运行,让output\exec命令也在等待它,然后check函数以未定义结束,因为输出尚未设置,同样的结果会一直返回给获得空页面的用户

现在我已经尝试了很多来自谷歌的combo与wait\async、promissions和其他巫术,但没有一个能帮助它正确同步,我想我需要做出连锁承诺来让它同步,但我不知道具体是如何实现的,因为check函数也被其他路由使用,它是一个通用函数,我真的不想为了处理这种奇怪的情况而复制代码


好了,就这样吧,非常感谢您的帮助。

我正在用两个选项和错误处理修改我的答案。您可以通过两种方式等待“secondFunction”执行

  • 用承诺来包装执行官
  • 使用回调
  • 选项1:用承诺包装

    async function checkFunction(req, res, next) {
        if('check') {
            try{
                await secondFunction(req, res, next);
                next();
            }catch(ex) {
                throw new Error('Error', ex);
            }
        }else{
            next(); //or throw error or redirect
        }
    }
    
    async function secondFunction(req, res, next) {
        const url = 'http://somepage.com';
        if ('some check on req') res.locals.output = 'nothing';
        else {
    
            try {
                const data = await axios.get(url);
                res.locals.output = 'good request';
    
            } catch (error) {
                await new Promise((resolve, reject) => {
                    exec(`tail -n 1 file.log`, (error, stdout, stderr) => {
                        if(error) {
                            console.log('err:', error);
                            reject(error);
                        }else {
                            console.log('stdout:', stdout);
                            console.log('stderr:', stderr);
                            res.locals.output = stdout;
    
                            resolve();
                        }
                    });
                });
            }
        }
    }
    
    选项2:使用回调

    function checkFunction(req, res, next) {
        if('check') {
            secondFunction(req, res, (err) => {
                if(err) {
                    //or throw error or redirect
                }else{
                    next();
                }
            });
    
        }else{
            next(); //or throw error or redirect
        }
    }
    
    function secondFunction(req, res, callback) {
        const url = 'http://somepage.com';
        if ('some check on req') {
            res.locals.output = 'nothing';
            callback();
        } else {
            axios.get(url).then((response) => {
                res.locals.output = 'good request';
                callback();
            }).catch(err => {
                exec(`tail -n 1 file.log`, (error, stdout, stderr) => {
                    if (error) {
                        console.log('err:', error);
                        callback(error);
                    } else {
                        console.log('stdout:', stdout);
                        console.log('stderr:', stderr);
                        res.locals.output = stdout;
    
                        callback();
                    }
                });
            });
        }
    }
    
    另外,child_process.execSync()方法通常与child_process.exec()相同,只是在子进程完全关闭之前,该方法不会返回

    var logs = execSync(`tail -n 1 file.log`);
    console.log(logs);
    

    但请注意,这将阻止您的IO操作。

    我正在使用两个选项和错误处理修改我的答案。您可以通过两种方式等待“secondFunction”执行

  • 用承诺来包装执行官
  • 使用回调
  • 选项1:用承诺包装

    async function checkFunction(req, res, next) {
        if('check') {
            try{
                await secondFunction(req, res, next);
                next();
            }catch(ex) {
                throw new Error('Error', ex);
            }
        }else{
            next(); //or throw error or redirect
        }
    }
    
    async function secondFunction(req, res, next) {
        const url = 'http://somepage.com';
        if ('some check on req') res.locals.output = 'nothing';
        else {
    
            try {
                const data = await axios.get(url);
                res.locals.output = 'good request';
    
            } catch (error) {
                await new Promise((resolve, reject) => {
                    exec(`tail -n 1 file.log`, (error, stdout, stderr) => {
                        if(error) {
                            console.log('err:', error);
                            reject(error);
                        }else {
                            console.log('stdout:', stdout);
                            console.log('stderr:', stderr);
                            res.locals.output = stdout;
    
                            resolve();
                        }
                    });
                });
            }
        }
    }
    
    选项2:使用回调

    function checkFunction(req, res, next) {
        if('check') {
            secondFunction(req, res, (err) => {
                if(err) {
                    //or throw error or redirect
                }else{
                    next();
                }
            });
    
        }else{
            next(); //or throw error or redirect
        }
    }
    
    function secondFunction(req, res, callback) {
        const url = 'http://somepage.com';
        if ('some check on req') {
            res.locals.output = 'nothing';
            callback();
        } else {
            axios.get(url).then((response) => {
                res.locals.output = 'good request';
                callback();
            }).catch(err => {
                exec(`tail -n 1 file.log`, (error, stdout, stderr) => {
                    if (error) {
                        console.log('err:', error);
                        callback(error);
                    } else {
                        console.log('stdout:', stdout);
                        console.log('stderr:', stderr);
                        res.locals.output = stdout;
    
                        callback();
                    }
                });
            });
        }
    }
    
    另外,child_process.execSync()方法通常与child_process.exec()相同,只是在子进程完全关闭之前,该方法不会返回

    var logs = execSync(`tail -n 1 file.log`);
    console.log(logs);
    

    但请注意,这将阻止您的IO操作。

    很抱歉,在发布问题时忘记添加下一个函数,checkFunction确实有它,因为它是一个中间件函数,但对于第二个函数,我不明白其意义,如果它不是中间件函数,它将返回checkFunction,我错了吗?好的,只是仔细检查一下,试试你的建议。我从路由的回调函数得到了更早的响应,所以我假设下一个函数也是一个异步函数,通过调用它,我实际上迫使它转到下一个不存在的中间件,因此它首先到达回调,然后按照执行顺序解析checkFunction和second函数……我做了一些工作对答案的修改。请看一看。
    secondFunction()
    将需要
    返回
    等待
    您对
    exec()的承诺。实际上,
    secondFunction()
    exec()
    完成后仍然无法进行通信。@nuwan niroshana谢谢!它最终可以根据需要工作。你不知道我花了多少时间在这件事上,以至于事实上我仍然不明白答案。哈哈。你能澄清一下我的想法吗:1。调用secondFunction()之后,我仍然看不到next()的意义,因为我说过checkFunction是一个全局中间件,所以在运行secondFunction的语句之后,它仍然有事情要做并运行,最后它运行next,在中间件完成之前运行next有什么原因吗?secondFunction只是一个可以在需要时使用的附加值。很抱歉,在发布问题时忘记添加下一个函数,checkFunction确实有它,因为它是一个中间件函数,但对于secondFunction,我不明白其意义,如果它不是中间件函数,它将返回checkFunction,我错了吗?好的,只是仔细检查一下,试试你的建议。我从路由的回调函数得到了更早的响应,所以我假设下一个函数也是一个异步函数,通过调用它,我实际上迫使它转到下一个不存在的中间件,因此它首先到达回调,然后按照执行顺序解析checkFunction和second函数……我做了一些工作对答案的修改。请看一看。
    secondFunction()
    将需要
    返回
    等待
    您对
    exec()的承诺。实际上,
    secondFunction()
    exec()
    完成后仍然无法进行通信。@nuwan niroshana谢谢!它最终可以根据需要工作。你不知道我花了多少时间在这件事上,以至于事实上我仍然不明白答案。哈哈。你能澄清一下我的想法吗:1。调用secondFunction()之后,我仍然看不到next()的意义,因为我说过checkFunction是一个全局中间件,所以在运行secondFunction的语句之后,它仍然有事情要做并运行,最后它运行next,在中间件完成之前运行next有什么原因吗?secondFunction只是一个附加值,可以在需要时使用。