【问题标题】:Express Middleware, how do I skip last function call?Express 中间件,我如何跳过最后一个函数调用?
【发布时间】:2019-07-05 01:17:54
【问题描述】:

我在 Node.js 中使用 express,并且在 get 路由中使用了一个函数... 该函数首先实现了自己编码的简单缓存功能,然后它从 MSSQL 数据库中查询一些数据并在 res.json(data) 中返回。但我想重构我的兑现函数,并将其放在自己的函数中,并将其称为快速中间件。但不知何故,我尝试了很多,但真的不知道该怎么做。下面是我重构的函数 checkTime()。

router.get("/v1/watch/readindex", async function(req,res) {
 ///////////////////////////CASHING///////////////////////////////////
    var timediff = config.get('write.cachetime'); //get cachetime - same for write or read
    timediff = 30000; //testing
    var timenow = Date.now();         
    if (!cachedtimeread || !readmsg || (cachedtimeread < (timenow-timediff))) {
        readmsg = "";
        cachedtimeread = timenow;  
        readtimemsg = `Zeit vergangen seit letztem Aufruf:  ${msToTime(cachedtimeread -timenow)}. API wurde aufgerufen.\n<br>`;
    }       
    else if (cachedtimeread >= (timenow - timediff)) { //API-Aufruf jünger als Aufrufzeitpunkt - timediff
        readmsg += `Zeit vergangen seit letztem Aufruf:  ${msToTime(cachedtimeread -timenow)}. Dokument ist aus dem Cache!\n<br>`; 
        //console.log("Funktion für Dokument-Query wurde nicht aufgerufen!");
        res.set('Content-Type', 'text/html');
        res.json(readmsg);
        return;
    } 
   //////////////////////////////////////////////////////////////////////
    axios.all([read = await count_ES_read(), await mssqlQuery(mssqlQueryLessOneDay), 
        await mssqlQuery(mssqlQueryLessEightHours), await mssqlQuery(mssqlQueryTotal) ])
    .then(axios.spread(function (resultES_read, mssqlLessOneDay, mssqlLessEightHours, mssqlTotal) {  
        readmsg=''; 
        var elasticsearchcount = resultES_read.count;

        var mssqlLessOneDay = mssqlLessOneDay.recordset[0].count;
        var mssqlLessEightHours = mssqlLessEightHours.recordset[0].count;
        var mssqlTotal = mssqlTotal.recordset[0].count;

        //console.log(elasticsearchcount, mssqlLessOneDay, mssqlLessEightHours, mssqlTotal);
        readmsg += `Umgebung: ${process.env.NODE_ENV} READ<br>`;
        readmsg += `ES Dokumente insg:  ${elasticsearchcount}  MSSQL Dokumente insg:  ${mssqlTotal} <br>`;
        if ( elasticsearchcount < mssqlLessOneDay) {
            readmsg += `Critical:   ${mssqlLessOneDay - elasticsearchcount} , weniger Dokumente in ES als im Dokumente-Pool die älter als 1 Tag sind. <br>`; 
        } if ((elasticsearchcount < mssqlLessEightHours))  {            
            readmsg += `Warning: ${mssqlLessEightHours - elasticsearchcount} , weniger Dokumente in ES als im Dokumente-Pool, die älter als 8 Stunden sind. <br>`;
        } if ((elasticsearchcount > mssqlTotal))  {  
            readmsg += `Achtung: ES-Dokumente Anzahl > Dokumente im Dokumente-Pool. Es gibt ${elasticsearchcount - mssqlTotal} ungelöschte Dokumente im Elasticsearch Index!`;
        }                          
        res.set('Content-Type', 'text/html');
        res.json(readmsg);
        })).catch((err) => {
            res.send(err);
        });
});

这个函数应该从上面最后一个异步函数的数据库查询中检索消息的输出,以防最后一个 api-route 调用的时间戳仍然比某个时间戳更早(以毫秒为单位)。如果不是这种情况,则应执行最后一次检索数据的函数调用。 但我不知道如何从异步函数中检索消息变量,将其保存在res.locals 中不知何故不起作用,此外,我不知道如何跳过最后一个函数调用。不知何故,中间件中 res.json() 之后的返回不起作用,并且始终执行 async-function,但是我想退出 checkTime() 函数并在文档更年轻时返回 res.json 中的消息now-timestamp.

function checkTime(writemsg) {
        return function(req,res,next) {
        var timediff = config.get('write.cachetime'); //get cachetime - same for write or read
        timediff = 30000; //testing
        var timenow = Date.now();         
        if (!cachedtimewrite || !writemsg || (cachedtimewrite < (timenow-timediff))) {
            writemsg = "";
            cachedtimewrite = timenow;  
            writetimemsg = `Zeit vergangen seit letztem Aufruf:  ${msToTime(cachedtimewrite -timenow)}. API wurde aufgerufen.\n<br>`;
            next();
        }       
        else if (cachedtimewrite >= (timenow - timediff)) { //API-Aufruf jünger als Aufrufzeitpunkt - timediff
            writemsg += `Zeit vergangen seit letztem Aufruf:  ${msToTime(cachedtimewrite -timenow)}. Dokument ist aus dem Cache!\n<br>`; 
            //console.log("Funktion für Dokument-Query wurde nicht aufgerufen!");
            res.set('Content-Type', 'text/html');
            res.json( writemsg);
            return;
        } 
    }
}


router.get("/v1/watch/writeindex", checkTime(writemsg), async function(req,res,next) { 

/////////////编辑:

我在 Promise.all() 中用于从 db 获取数据的函数之一是:

var mssqlQuery = (query) => {  
    if (!query || query =="") {
        throw new Error('Query for MSSQL was not defined!');        
    }
    //console.log("Query: ", query);
    var conn = new sql.ConnectionPool(mssqlconfig);
    var req = new sql.Request(conn);
    return  conn.connect().then(async() => {       
            var result = await req.query(query);
            //conn.close();
            return result;            
            //conn.close();
           }).catch(e => {
                return e;
           }).finally(() => {
                conn.close();
           });   
}

【问题讨论】:

    标签: node.js express asynchronous middleware next


    【解决方案1】:

    或者我无法理解您的问题,或者您不太了解 express 中间件的工作原理。

    function checkTime(writemsg) {
      return function(req,res,next) {
        if(foo) {
          req.storeValueKey = 'data to store'
          next(); // run next middleware
        } else {
          res.json('stop processing');
        }
      }
    }
    router.get('/url',checkTime('message'), (req, res) => {
      console.log('data form middleware', req.storeValueKey)
      res.json('send data collected from DB' + req.storeValueKey);
      req.session.storeValueForNextRequests = 'val';
    })
    

    //编辑开始

    在中间件中,您可以使用next()res.send()。两者都使用是错误的。而且你必须记住next()res.send()的使用不会中断父函数的执行。 (记住return

    res.send('Text response');
    next();
    thisIsAlsoRun();
    

    而且您知道,在您的中间件中存在“黑洞”。如果两个if 都是false 你卡在中间件中,所以添加else next() 以避免这种情况

    //编辑结束

    另外,我还被一个问题困扰:为什么要使用 HTTP 客户端从 Promise 中收集数据,为什么要在那里使用 await?

    节点应该一个一个地做,而不是异步的。 (除非它更聪明并忽略这个错误的语法)。

    一开始,在Promise.all中停止使用await

    然后使用native Promises instead of those from the axios http 客户端。

    Promise.all([count_ES_read(),mssqlQuery(mssqlQueryLessOneDay), 
            mssqlQuery(mssqlQueryLessEightHours), mssqlQuery(mssqlQueryTotal) ])
        .then(function (resultES_read, mssqlLessOneDay, mssqlLessEightHours, mssqlTotal) {
            req.storeValueKey ; 
        }) 
    

    //编辑2

    我发现您在理解 async/awaitPromisses 时遇到了很大的问题。节点基于事件(例如获取结果)

    非常笼统的await 的意思是:“等到你继续执行函数,直到得到结果。”和Promisses:“创建一个可以处理@987654340 的管道@ 并进一步处理指令。”

    使用Promise.all() 的意义在于您将Promisess 放在那里。但是您正在使用 await

    来塑造执行
    const mssqlQuery = (query) => {  
        if (!query || query =="") {
            throw new Error('Query for MSSQL was not defined!');        
        }
        //I will not investigate whether you are doing well making a call with each request.
        const conn = new sql.ConnectionPool(mssqlconfig);
        const req = new sql.Request(conn);
        return  conn.connect().then(() => {
                // Promise can return promise
                let result = req.query(query);
                conn.close();
                return result;            
            });   
    }
    const mssqlNoErrorQuery = (query) => {
        return mssqlQuery(query).catch( e => { 
            return null; // no results 
            // OR fake resultset count
            return {recordset:[{count:null}]};
            /*
            return e; // this will cause the error in the next stage 
            //to be treated as a correct situation.
            // Next you should take care of it in such a way:
            let res = await mssqlQuery(query);
            if(res instanceof Error)
                console.error('something wrong')
            else    
                res.recordset[0].count
            */
        });
    }
    
    let [res1, res2] = await Promise.all([
        mssqlNoErrorQuery(query1),
        mssqlQuery(query2).catch(e =>{/* local error handling */})
    ])
    

    为了更好地理解整个想法,你应该用这段代码做一些实验:

    function sleep(ms){
        return new Promise(resolve=>{
            setTimeout(resolve,ms)
        })
    }
    
    function wait(msg) {
        return new Promise(async(resolve) => {
            console.log('START '+ msg)
            await sleep(700)
            console.log('finish '+ msg)
            resolve(true)
        } )
    }
    
    let fo = async () => {
        Promise.all([await wait('d'), await wait('e'), await wait('f')]).then((c,d,e)=> {console.log('collect CDE')})
    
        Promise.all([wait('a'),wait('b'),wait('c')]).then((c,d,e)=> {console.log('collect ABC')})
    }
    
    fo();
    

    结果:

    START d  12:22:06 PM
    finish d 12:22:07 PM
    START e  12:22:07 PM
    finish e 12:22:07 PM
    START f  12:22:08 PM
    finish f 12:22:08 PM
    START a  12:22:08 PM
    START b  12:22:08 PM
    START c  12:22:08 PM
    collect CDE
    finish a  12:22:09 PM
    finish b  12:22:09 PM
    finish c  12:22:09 PM
    collect ABC
    

    顺便说一句:您是否注意到进程 a、b、c 在从 c、d、e 收集结果之前开始?

    现在改为:

    let fo = async () => {
        Promise.all([wait('a'),wait('b'),wait('c')]).then((c,d,e)=> {console.log('collect ABC')})
        Promise.all([await wait('d'), await wait('e'), await wait('f')]).then((c,d,e)=> {console.log('collect CDE')})
    }
    

    结果:

    START a
    START b
    START c
    START d
    finish a
    finish b
    finish c
    finish d
    collect ABC
    START e
    finish e
    START f
    finish f
    collect CDE
    

    正如您在此处看到的,所有承诺从第一次调用开始,从第二次调用开始

    【讨论】:

    • 您几乎了解所有内容,除了我在上次函数调用中有另一个 res.json,并且根据中间件函数的条件,我要么想在中间件中返回我的 res.json 响应,然后不在我的最后一个函数中执行 res.json,或者我想跳过中间件,并在最后一个函数中执行 res.json ......如果我同时做这两个,我得到一个“无法设置标题发送后..”
    • 另外,我在 axios.all([...]) 里面做数据库查询,那我为什么不做呢?
    • 您不应该这样做,因为这是一种不好的做法。不在all() 中执行查询,而是使用axios。几年前 promises 在 Node 中还不能天真地使用,然后就可以了。但现在它们是,所以请使用本机语法。 (并且有计划在 axios 1.0 中删除此帮助程序,请参阅 lnk)+ 查看更新答案
    • 我编辑了其中一个函数来检索我的帖子中的数据。为什么我应该按顺序而不是并行从 db 中获取数据?我的数据库请求大约需要 1 分钟,而我必须完成其中的 4 个,所以如果我不并行执行它会花费很多时间。为什么我不能在 Promise.all() 中执行请求?而为什么不在axios中使用“await function()”来等待结果呢,有没有别的办法呢?我不知道怎么做..
    • 当您使用await 时,您按顺序获取数据。检查答案。
    猜你喜欢
    • 1970-01-01
    • 2021-02-23
    • 2018-06-08
    • 2016-03-31
    • 2014-05-24
    • 2012-02-10
    • 2020-04-08
    • 2017-07-27
    • 2015-09-10
    相关资源
    最近更新 更多