如何将链式 Promise 与数组的 for 循环一起使用?

Posted

技术标签:

【中文标题】如何将链式 Promise 与数组的 for 循环一起使用?【英文标题】:How to use chaining promise together with for loops of arrays? 【发布时间】:2019-07-02 09:55:51 【问题描述】:

所以,我有这个代码。我正在尝试永远调试它。我不知道为什么它返回错误。我可能忘记了一些承诺,或者我的 array.push 有问题,因为当我查看日志时,它会在我将一些对象推入数组的地方抛出错误。

到目前为止,这是我的代码:

router.post('/inventory/product/stocks/add/(:id)', authenticationMiddleware(), function(req, res, next) 
const db = require('../db.js')

var product_no = req.params.id
var cog = req.body.cog
var size_slug = req.body.size_slug
var size_name = req.body.size_name
var rowinserted = 0
var initial_stock = req.body.initial_stock
var stock_id = new Array
var batch_id = new Array
var stock = new Array
var batch = new Array

new Promise(function(resolve, reject) 
    console.log('one');
    // Getting product product_slug for product_sku

    let sql = `SELECT product_slug
               FROM inventory_tbl
               WHERE product_no = ?`

    db.query(sql, [req.params.id], (error, results, fields) => 
        if (error) 
            throw error;
         else 
            var product_slug = results[0].product_slug
            resolve(product_slug)
        
    )
)
.then(function(value) 
    console.log('two');
    // Insert product sizes together with its initial stock

    for (var x = 0; x < size_slug.length; x++) 
        var product_sku = value + size_slug[x]
        var slug = size_slug[x]
        var name = size_name[x]
        var initial_stock = initial_stock[x]
        console.log(product_sku);
        if (size_slug[x] != '') 
            stock.push(
                product_sku: product_sku,
                product_no: product_no,
                size_slug: slug,
                size_name: name,
                total_stock: initial_stock,
                available_stock: initial_stock
            )
        
        console.log(stock);
    

    for (var x = 0; x < size_slug.length; x++) 
        var product_sku = value + size_slug[x]
        var initial_stock = initial_stock[x]
        if (size_slug[x] != '') 
            batch.push(
                product_no: product_no,
                product_sku: product_sku,
                production_date: mysql.raw('CURRENT_TIMESTAMP'),
                batch_cog: cog,
                initial_stock: initial_stock,
                stock_left: initial_stock
            )
        
        console.log(batch);
    

    return value
)
.then(function(value) 
    console.log('three');
    // Insert rows to product_tbl and stock_tbl

    for (var i = 0; i < stock.length; i++) 
        let sql = `INSERT INTO product_tbl(product_sku, product_no, size_slug, size_name, total_stock, available_stock) VALUES (?, ?, ?, ?, ?, ?)`

        db.query(sql, [stock[i].product_sku, req.params.id, stock[i].size_slug, stock[i].size_name, stock[i].total_stock, stock[i].available_stock], (error, results, fields) => 
            if (error) throw error
            db.query(`SELECT LAST_INSERT_ID() AS id;`, (error, results, fields) => 
                stock_id[i] = results[0].id
            )
        )

        sql = `INSERT INTO stocks_tbl(product_no, product_sku, production_date, batch_cog, initial_stock, stock_left) VALUES (?, ?, CURRENT_DATE, ?, ?, ?)`

        db.query(sql, [req.params.id, batch[i].product_sku, batch[i].batch_cog, batch[i].initial_stock, batch[i].stock_left], (error, results, fields) => 
            if (error) throw error
            db.query(`SELECT LAST_INSERT_ID() AS id;`, (error, results, fields) => 
                batch_id[i] = results[0].id
            )
        )
        rowsupdated++
    

    return value
)
.then(function(value) 
    console.log('four');
    // Render the web page
    if (rowinserted != sizeslug.length) 
        req.flash('error', error)

        res.redirect('/admin/inventory/product/stock/add/' + req.params.id)
     else 
        req.flash('success', 'Data added successfully!')
        res.redirect('/admin/inventory/product/stock/add/' + req.params.id)
    
)
.catch(function(error) 
    console.log('error');
    // Error handler

    for (var i = 0; i < rowinserted; i++) 
        let sql = `DELETE FROM product_tbl WHERE product_sku = ?`

        db.query(sql, [stock_id[i]], (error, results, fields) => 
            if (error) throw error
        )

        sql = `DELETE FROM stocks_tbl WHERE product_sku = ?`

        db.query(sql, [batch_id[i]], (error, results, fields) => 
            if (error) throw error
        )
    

    res.redirect('/admin/inventory/product/stock/add/' + req.params.id)
)
)

我的日志返回:

一个

两个

错误

编辑:进程在 console.log('two') 之后停止(我不确定具体的行,但根据日志输出),因为我尝试在for 循环,但它们不会在那里继续。它只是转到 .catch/error。

【问题讨论】:

您是否考虑过为此使用库?如果是,我建议使用when。它真的很强大。 github.com/cujojs/when 如果你返回的value参数不是一个promise,那么你就不能真正用.then链接函数,因为它没有返回一个promise @Strawberry JS 有三个不同的引号,单引号、双引号和反引号。 Single 和 double 没有行为差异,但反引号允许您执行多行字符串和模板。 @MichaelWestcott 够公平的! 回调地狱 :) 如果您使用过任何库,例如 Q 或 bluebird 甚至 async/await(if nodejs>6.11),那么您将永远不会遇到这样的错误。 【参考方案1】:

而不是在console.log('error'); 中输出字符串,而是转储您在catch 处理程序中收到的实际error 对象。它将提供有关失败原因和位置的更多详细信息。我怀疑console.log('two');之后的代码会抛出异常,然后你不小心把它吞到下面了。

考虑将您的代码拆分为单独的主题函数。这样您就可以更轻松地维护和发现错误(或拼写错误)。

【讨论】:

【参考方案2】:

查看输出,我可以看到 console.log(product_sku); 这没有被打印出来。所以,实际上问题是var initial_stock = initial_stock[x]。您已经声明了与全局变量(到您的 route.post 回调函数)同名的局部变量(到您的 then 回调函数),现在您的全局 initial_stock 变量被本地变量掩盖,这不是一个数组(实际上是不明确的)。因此,尝试将变量名称更改为 then 块中的其他名称,看看问题是否消失。

希望这会有所帮助。

【讨论】:

没有窗口,代码是node.js。无论如何, intial_stock 不是全局的,它的范围是 router.post 回调函数。除此之外,您可能走在正确的轨道上 啊,我明白了。我想我错过了上述上下文并专注于有问题的代码。我将编辑我的答案。谢谢@MichaelWestcott

以上是关于如何将链式 Promise 与数组的 for 循环一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

解决for循环中异步请求顺序不一致的问题

将嵌套循环查询组合到父数组结果 - pg-promise

如何在 Promise 中使用循环

使用fetch时如何在for循环中动态循环多个promise?

深入理解 promise:promise的三种状态与链式调用

ES7中的async和await