javascript - 在我的 Nodejs Controller 中 promise 不会互相等待

标签 javascript node.js promise

我这里有 4 个 promise ,我认为它会运行第一个 promise ,然后等到它完成,然后运行下一个,等到完成然后运行下一个等等..

但是这里发生的情况是它同时运行所有这些,并且不等待任何事情完成。

这是我的 promise 链:

// 
// 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=====

我理解的 promise 是正确的还是?

无论如何,这是我的 Nodejs Controller :

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

最佳答案

您需要将 Promise 包装在函数中

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 定义的 Promise 得到解决,这些方法将用于继续您的流程。它可以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() 返回的 Promise 已满,但执行器没有将任何值传递给解析回调 resolveCB2。在第二种情况下,resolveCB3 中发生异常,它被拒绝,因此调用 rejectCB2。如果我们希望解析回调接收参数,我们必须通知执行器。为此,最简单的方法是让回调返回一个值:

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() 方法。这不是一个 promise 。你不能执行resolveCB.then(resolveLastCB),它会抛出一个错误resolveCB.then(不是一个函数或类似的东西。你可能认为这是一个严重的错误,我没有调用resolveCBresolveCB().then(resolveLastCB) 应该可以工作吗?对于那些考虑过这一点的人来说不幸的是,它仍然是错误的。resolveCB 返回一个字符串,一些字符,不是 Promise

为了避免此类维护问题,您应该知道解析和拒绝回调可以返回 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);

关于javascript - 在我的 Nodejs Controller 中 promise 不会互相等待,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44083969/

相关文章:

javascript - 通过从选项 html 中进行选择来打开新选项卡

javascript - 我无法理解 promise

Jquery 具有多个 Promise 时

node.js - 使用 expressjs 处理异步 waterfall 中的错误

javascript - 如何打破从内部 promise 函数触发的循环?

javascript - 使用javascript的文件的MD5哈希

javascript - setInterval() 在clearInterval() 之后不起作用

javascript - 无法翻转多个元素

javascript - 如何在 JavaScript 中正确引用 Oracle SQL 字符串

node.js - Nodejs process.stdin.resume() 在 Cygwin 中不起作用