簡體   English   中英

在我的Node.js控制器中不等待對方

[英]Promises not waiting for each other in my nodejs controller

我在這里有4個諾言,我認為它將執行第一個諾言,然后等到其完成,然后再執行下一個諾言,等到完成並再執行下一個諾言,依此類推。

但是這里發生的是它一次運行所有這些,並且不等待任何事情完成。

這是我的諾言鏈:

// 
// Run the promises
// 
findBanks
    .then(findReceipts)
    .then(findExpenses)
    .then(sendResult)
    .catch(err => {
        console.error(err);
        console.log("getbankAccountReport ERR: " + err);
        res.json({error:true,err})
    });

這是我的console.log的輸出

=====findAllBank=====
=====findAllReceipt=====
=====findAllExpense=====
=====RESOLVE findAllBank=====
=====sendResult=====
=====RESOLVE sendResult=====
=====RESOLVE findAllReceipt=====
=====RESOLVE findAllExpense=====

我不理解諾言正確嗎?

無論如何,這是我的nodejs控制器:

    exports.getBankAccountReport = function(req, res) {

        // 
        // Find all bank accounts
        // 
        var bankModel = require('../models/bankModel');
        var bankTable = mongoose.model('bankModel');
        var bankArray = [];
        var findAllBank = new Promise(
            (resolve, reject) => {
            console.log("=====findAllBank=====")
            bankTable.aggregate([
                ...lots of mongo stuff...
                ],function(err, data) {
                if (!err) {
                    bankArray = data;
                    console.log("=====RESOLVE findAllBank=====")
                    resolve(data);
                } else {
                    reject(new Error('findBank ERROR : ' + err));
                }
            });
        });


        // 
        // Find the RECEIPT for each bank account
        // 
        var receiptModel = require('../models/receiptModel');
        var receiptTable = mongoose.model('receiptModel');
        var receiptArray = [];
        var findAllReceipt = new Promise(
            (resolve, reject) => {
            console.log("=====findAllReceipt=====")
            receiptTable.aggregate([
                ...lots of mongo stuff...
                ], function (err, data) {
                    if (!err) {
                        receiptArray = data;
                        console.log("=====RESOLVE findAllReceipt=====")
                        resolve(data);
                    } else {
                        reject(new Error('findReceipts ERROR : ' + err));
                    }
            });
        });


        // 
        // Find the EXPENSE for each bank account
        // 
        var expenseModel = require('../models/expenseModel');
        var expenseTable = mongoose.model('expenseModel');
        var expenseArray = [];
        var findAllExpense = new Promise(
            (resolve, reject) => {
            console.log("=====findAllExpense=====")
            expenseTable.aggregate([
                ...lots of mongo stuff...
                ], function (err, data) {
                    if (!err) {
                        expenseArray = data;
                        console.log("=====RESOLVE findAllExpense=====")
                        resolve(data);
                    } else {
                        reject(new Error('findExpense ERROR : ' + err));
                    }
            });
        });

        var sendResult = function(data) {
            var promise = new Promise(function(resolve, reject){
                console.log("=====sendResult=====")
                res.json({error:false,  
                          "bank":bankArray, 
                          "receipt":receiptArray, 
                          "expense":expenseArray})
                console.log("=====RESOLVE sendResult=====")
                resolve();
            });
            return promise;
        };

    // 
    // Run the promises
    // 
    findAllBank
        .then(findAllReceipt)
        .then(findAllExpense)
        .then(sendResult)
        .catch(err => {
            console.error(err);
            console.log("getbankAccountReport ERR: " + err);
            res.json({error:true,err})
        });
}

您需要將Promises包裝在函數中

var findAllBank = function() {
    return new Promise(
        (resolve, reject) => {
        console.log("=====findAllBank=====")
        bankTable.aggregate([
            ...lots of mongo stuff...
            ],function(err, data) {
            if (!err) {
                bankArray = data;
                console.log("=====RESOLVE findAllBank=====")
                resolve(data);
            } else {
                reject(new Error('findBank ERROR : ' + err));
            }
        });
    });
});

解析后,將使用resolve()函數中傳遞的數據來調用鏈中的下一個函數。

不要混淆Promise和構建它的功能

創建new Promise(executor) ,您將實例化一個新對象,該對象將具有兩種方法(對象的功能) .then(resolveCB [, rejectCB]).catch(rejectCB)

動漫是要知道,每當一個過程完成時,它是否成功或失敗,然后相應地繼續。

var myFirstPromise = new Promise(function executor(resolve, reject) { resolve('resolved'); });

換句話說,一旦executor定義的承諾得到解決,這些方法將用於繼續您的過程。 它既可以fulfilled並調用resolveCB回調(使用then ),也可以rejected並調用rejectCB回調(同時使用thencatch )。 回調(resolveCB或rejectCB)是一個函數,而不是Promise本身,即使該回調可能返回Promise。

myFirstPromise
    .then(function resolveCB(result) { console.log(result); }) //you can use a 2nd callback for rejection at this point
    .catch(function rejectCB(err) { console.log(err); });

myFirstPromise
    .then(
        function resolveCB(result) { console.log(result); } // if resolved
      , function rejectCB(err) { console.log(err); } // if rejected
    )
    .catch(function rejectCB(err) { console.log(err); }); // NEVER forget the last catch, just my 2cents :)

我們看到的輸入.then().catch()但對於它們的返回值? 他們兩個都將返回一個新的Promise。 這就是為什么你可以鏈.then()的和.catch()的。

myFirstPromise
    .then(function resolveCB1(result) { console.log(result); })
    .then(function resolveCB2(result) { console.log(result); }) // console.log is called but result is undefined
    .catch(function rejectCB1(err) { console.log(err); });
myFirstPromise
    .then(function resolveCB3(result) { 
        throw 'I threw an exception';  // an exception is thrown somewhere in the code
        console.log(result); 
    })
    .then(function resolveCB4(result) { console.log(result); })
    .catch(function rejectCB2(err) { console.log(err); }); // a promise in the chain get rejected or error occured

在前面的示例中,我們看到第二個.then()被命中,但result未定義。 第一個.then()已滿文件返回的承諾.then()但執行者未將其傳遞給resolve回調resolveCB2傳遞任何值。 在第二種情況下, resolveCB3發生了一個異常,該異常被拒絕,因此調用了rejectCB2 如果我們希望我們的解析回調接收一個參數,我們必須通知exector。 為此,最簡單的方法是使回調返回一個值:

myFirstPromise
    .then(function resolveCB1(result) {
        console.log(result);
        result += ' again';
        return result;
    })
    .then(function resolveCB2(result) { 
        console.log(result);
        return result;
    })
    .catch(function rejectCB1(err) { console.log(err); });

如此說來,您已經將所有的內容整合在一起以了解Promise 讓我們嘗試以一種更簡潔的方式總結一下:

var myFirstPromise = new Promise(function executor(resolve, reject) { resolve('resolved'); })
  , resolveCB = function resolveCB(result) {
        console.log(result);
        result += ' again';
        return result;
    }
  , resolveLastCB = function resolveLastCB(result) {
        console.log(result);
        result += ' and again';
        return result;
    }
  , justLog = function justLog(result) {
        console.log(result);
        return result;
    }
  ;
myFirstPromise
    .then(resolveCB)
    .then(resolveLastCB)
    .then(justLog)
    .catch(justLog);

您現在可以很好地對其進行贊美了,這很酷

myFirstPromise
    .then(resolveCB)
    .then(resolveCB)
    .then(resolveCB)
    .then(resolveCB)
    .then(resolveCB)
    .then(resolveCB)
    .then(resolveLastCB)
    .then(justLog)
    .catch(justLog);

但是,如果您的Promise鏈“確實”發生了變化,並且您需要擺脫myFirstPromise並從resolveCB開始,該怎么辦? 這只是一個功能,它可以執行,但沒有任何.then().catch()方法。 這不是一個承諾。 您不能做resolveCB.then(resolveLastCB) ,它將引發錯誤resolveCB.then(不是函數或類似的東西。您可能會認為這是一個嚴重的錯誤,我沒有調用resolveCBresolveCB().then(resolveLastCB)應該能工作嗎?對於那些想到這一點的人來說,還是很不幸的。它仍然是錯誤的resolveCB返回一個字符串,一些字符,而不是Promise

為了避免此類維護問題,您應該知道resolve和reject回調可以返回Promise而不是值。 為此,我們將使用所謂的工廠模式。 簡單來說,工廠模式就是使用(靜態)函數實例化新對象,而不是直接使用構造函數。

var myFirstPromiseFactory = function myFirstPromiseFactory() { 
        /*
        return new Promise(function executor(resolve, reject) {
            resolve('resolved'); 
        });

        if you just need to resolve a Promise, this is a quicker way
        */
        return Promise.resolve('resolved');
    }
  , resolveFactory = function resolveFactory(result) {
        return new Promise(function executor(resolve, reject) {             
            result = result || 'I started the chain';
            console.log(result);
            result += ' again';
            return resolve(result); // you can avoid the return keyword if you want, I use it as a matter of readability
        })
    }
  , resolveLastFactory = function resolveLastFactory(result) {
        return new Promise(function executor(resolve, reject) { 
            console.log(result);
            result += ' and again';
            return resolve(result);
        });
    }
  , justLogFactory = function justLogFactory(result) {          
        return new Promise(function executor(resolve, reject) { 
            console.log(result);
            return resolve(result);
        });
    }
  ;
myFirstPromiseFactory() //!\\ notice I call the function so it returns a new Promise, previously we started directly with a Promise
    .then(resolveFactory)
    .then(resolveLastFactory)
    .then(justLogFactory)
    .catch(justLogFactory);
// Now you can switch easily, just call the first one
resolveFactory()
    .then(resolveLastFactory)
    .then(justLogFactory)
    .catch(justLogFactory);

justLogFactory('I understand Javascript')
    .then(resolveLastFactory)
    .then(justLogFactory)
    .catch(justLogFactory);

在數組上迭代時,工廠函數可能會派上用場。 給定輸入,它可以用於產生一個promise數組:

var myFirstPromiseFactory = function myFirstPromiseFactory() { 
        /*
        return new Promise(function executor(resolve, reject) {
            resolve('resolved'); 
        });

        if you just need to resolve a Promise, this is a quicker way
        */
        return Promise.resolve('resolved');
    }
  , resolveFactory = function resolveFactory(result) {
        return new Promise(function executor(resolve, reject) {             
            result = result || 'I started the chain';
            console.log(result);
            result += ' again';
            return resolve(result); // you can avoid the return keyword if you want, I use it as a matter of readability
        })
    }
  , resolveLastFactory = function resolveLastFactory(result) {
        return new Promise(function executor(resolve, reject) { 
            console.log(result);
            result += ' and again';
            return resolve(result);
        });
    }
  , justLogFactory = function justLogFactory(result) {          
        return new Promise(function executor(resolve, reject) { 
            console.log(result);
            return resolve(result);
        });
    }
  , concatValues = function concatValues(values) {
        return Promise.resolve(values.join(' '));
    }
  , someInputs =  [
        'I am an input'
      , 'I am a second input'
      , 'I am a third input'
      , 'I am yet an other input'
    ]
  ;
myFirstPromiseFactory() //!\\ notice I call the function so it returns a new Promise, previously we started directly with a Promise
    .then(resolveFactory)
    .then(resolveLastFactory)
    .then(justLogFactory)
    .catch(justLogFactory);
// Now you can switch easily, just call the first one
resolveFactory()
    .then(resolveLastFactory)
    .then(justLogFactory)
    .catch(justLogFactory);

justLogFactory('I understand Javascript')
    .then(resolveLastFactory)
    .then(justLogFactory)
    .catch(justLogFactory);

// Using a factory functions to create an array of promise usable with Promise.all()
var promiseArray = someInputs.map(function(input) {
    return justLogFactory(input);
});
Promise.all(promiseArray)
    .then(concatValues)
    .then(resolveLastFactory)
    .then(justLogFactory)
    .catch(justLogFactory);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM