Express Middleware, как пропустить последний вызов функции?

Я использую выражение в Node.js, и я использую функцию внутри маршрута получения... Функция сначала реализует простую функцию кэширования с собственным кодом, а затем запрашивает некоторые данные из базы данных MSSQL и возвращает их в res.json (данные). Но я хотел бы реорганизовать свою функцию кэширования, поместить ее в собственную функцию и назвать ее промежуточным программным обеспечением Express. Но так или иначе, я пробовал много, но действительно не знаю, как это сделать. Ниже приведена моя переработанная функция 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-маршрута все еще моложе некоторой временной метки в миллисекундах. Если это не так, должен быть выполнен последний вызов функции для извлечения данных. Но я не знаю, как получить переменную сообщения из асинхронной функции, сохранить ее в res.locals как-то не получилось, и, более того, я не знаю, как тогда пропустить последний вызов функции. Почему-то возврат после res.json() в мидлваре не работает и асинхронная функция выполняется всегда, но я хочу выйти из функции 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() для получения данных из БД, такова:

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();
           });   
}

person ChillaBee    schedule 11.02.2019    source источник


Ответы (1)


Либо у меня проблемы с пониманием вашего вопроса, либо вы не совсем понимаете, как работает экспресс промежуточное ПО.

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-клиент для сбора данных из промисов и почему вы используете там ожидание?

Node должен делать это один за другим, а не асинхронно. (Если это не умнее и игнорирует этот неправильный синтаксис).

В начале прекратите использовать await в Promise.all.

Затем используйте собственный Promises вместо http-клиента axios.

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

//Редактировать 2

Я вижу, что у вас большие проблемы с пониманием async/await и Promisses. Узел основан на событиях (например, получение результатов)

Очень обобщающий await означает: "Подождите, пока вы продолжите выполнение функции, пока не получите результаты." И Promisses: "создайте конвейер, который будет обрабатыватьthen и обрабатывать инструкции дальше ."

Смысл использования Promise.all() заключается в том, что вы ставите туда Promises. Но вы формируете исполнение, используя 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

Как видите, все промисы начинаются с первого звонка, а фитст — со второго звонка.

person bato3    schedule 11.02.2019
comment
вы почти все поняли, кроме того, что у меня в последнем вызове функции есть другой res.json, и в зависимости от условия от функции промежуточного слоя я либо хочу вернуть свой ответ res.json в промежуточном программном обеспечении, а затем не выполнять res. json в моей последней функции, или я хочу пропустить промежуточное ПО и выполнить res.json в последней функции... Если я делаю и то, и другое, я получаю сообщение о невозможности установки заголовков после их отправки. - person ChillaBee; 11.02.2019
comment
кроме того, я делаю запросы к базе данных внутри axios.all([...]), так почему бы мне не сделать это? - person ChillaBee; 11.02.2019
comment
Вы не должны этого делать, потому что это плохая практика. Не выполнять запросы в all(), а использовать axios. Пару лет назад обещания наивно не были доступны в Node, а тогда все было в порядке. Но теперь они есть, поэтому, пожалуйста, используйте родной синтаксис. (И есть планы удалить эти помощники в axios 1.0, см. lnk) + см. ответы обновлений - person bato3; 12.02.2019
comment
Я отредактировал одну из функций для получения данных в своем сообщении. Почему я должен извлекать данные из БД последовательно, а не параллельно? Мой запрос к БД занимает около 1 минуты, и мне нужно выполнить 4 из них, поэтому это занимает много времени, если я не делаю это параллельно. И почему я не могу выполнять запросы в Promise.all()? И почему бы не использовать функцию ожидания () для ожидания результатов в аксиомах, есть ли другой способ сделать это? я не знала как.. - person ChillaBee; 12.02.2019
comment
Вы извлекаете данные последовательно, когда используете await. Проверить ответ. - person bato3; 12.02.2019
comment
хорошо, спасибо, я этого не знал, поэтому я заменил функцию ожидания () просто обработчиком функции внутри функции Promise.all (), которая должна делать это параллельно, верно? Или есть другой способ одновременного выполнения асинхронных вызовов? - person ChillaBee; 15.02.2019
comment
Да. Кстати: помните, что .then(()=> {reurn await}) можно заменить на then().then() Promise может вернуть следующее обещание, а не только плоский результат - person bato3; 15.02.2019