【问题标题】:Async in for loop, Nodejsfor循环中的异步,Nodejs
【发布时间】:2019-02-15 11:52:50
【问题描述】:

我希望 for 循环按顺序运行,在移动到下一个循环之前完全完成一个循环。该循环将一条 JSON 消息放入另一个 JSON 消息中,然后将其发送到一个开始发布到 api 的函数。在转到 JSON 中的下一项之前,我需要完成该功能。 p 是用于回发的项目名称,是否通过 api 服务成功发布到数据库。

这里是这个问题的简化代码。

let processJson = function(items) {
    for (const p in items) {
        let newObj = {
            "key1": items[p].key1,
            "key2": items[p].key2,
            "keySpecial": items[p].key3 + items[p].key4
        };
        await validateJson(p, newObj);
    }
};

我需要 validateJson 完成它的异步工作链,然后再继续循环中的下一个 p。

我该怎么做?

这里是请求的 validateJson 函数。

const validateJson = function (id, jsonObj) {
    const processItemSchema = {
        "properties": {
            "key1": {
                "type": "string"
            },
            "key2": {
                "type": "string",
                "minLength": 3,
                "maxLength": 3
            },
            "keySpecial": {
                "type": "string",
                "minLength": 4,
                "maxLength": 4
            }
        }
    };
    const ajv = new Ajv();
    let validate = ajv.compile(processItemSchema);
    let valid = validate(jsonObj);
    if (!valid){
        resCallback(id + ": invalid JSON");
    }
    else{
        // Generate Special Flag(s) value, Comma Separated Value
        let specialFlag = "";
        specialFlag += specialCheck1(jsonObj.keySpecial);
        if(specialFlag.length > 0) {
            let temp = specialCheck2(jsonObj.keySpecial);
            if (temp.length > 0) {
                specialCheck += "," + temp;
                maintenanceCall(id, jsonObj, specialFlag);
            }
            else {
                mainenanceCall(id, jsonObj, specialFlag);
            }
        }
        else {
            specialFlag += specialCheck1(jsonObj.keySpecial);
            maintenanceCall(id, jsonObj, specialFlag);
        }
    }
};

根据要求提供更多代码

const maintenanceCall= function (id, jsonObj, specialFlag) {
        request.post({
            url: 'https://url.poster/something',
            auth: {
                'user': 'user',
                'pass': 'pass',
                'sendImmediately': true
            },
            json: true,
            body: {
                "Input": {
                    "InputParameters": {
                        "KEY": jsonObj.key1,
                        "Hole": jsonObj.Key2,
                        "SomeWhere": jsonObj.keySpecial
                    }
                }
            }
        }
        , function (error, response, body) {
            if (body.OutputParameters.X_MSG_DATA !== null) {
                resCallback(id + , Message: "
                    + body.OutputParameters.DATA);
            }
            else {
                const sampCheck = function(smsFlag){
                    if(flag=== "Y")
                        return ".X";
                    else if(flag=== "N")
                        return "";
                    else
                        resCallback(id + ": this item can not be processed");
                    processItem(id, jsonObj, stats);
                }
            }
        });
};

【问题讨论】:

  • 可以发validateJson的代码吗?它需要改变。
  • 请贴出实际代码,以便确定解决方案。
  • 我没有看到任何异步操作,它们必须在resCallbackmaintenanceCall 中?
  • resCallback() 是做什么的?是同步的还是异步的?
  • 另外,processItem() 是做什么的,是同步的还是异步的?

标签: json node.js asynchronous request async-await


【解决方案1】:

适用于那些不想重写和重组所有代码的人。对于那些不想要不必要的复杂性的人。对于那些希望他们的 for 循环中的项目在下一个项目循环之前完成发布的人。对于那些喜欢保持简单的人。对于这些.. 在这里。

/**
 * Clever way to do asynchronous sleep. 
 * Check this: https://stackoverflow.com/a/46720712/778272
 *
 * @param {Number} millis - how long to sleep in milliseconds
 * @return {Promise<void>}
 */
async function sleep(millis) {
    return new Promise(resolve => setTimeout(resolve, millis));
}

async function run() {
    const urls = await fetchUrls(INITIAL_URL);
    for (const url of urls) {
        await sleep(10000);
        const $ = await fetchPage(url);
        // do stuff with cheerio-processed page
    }
}

【讨论】:

    【解决方案2】:

    要使用await“暂停”您的for 循环,您必须等待一个promise。所以你必须让validateJson() 返回一个承诺,当该函数内的任何异步操作完成时,该承诺就会解决。这就是 async/await 在 Javascript 中的工作方式。

    目前尚不清楚validateJson() 中究竟什么是异步的或不是异步的。如果没有什么是异步的,那么它只是串行执行,你根本不需要await 或承诺。 Javascript 是单线程的,所以它只会运行 validateJson() 直到它完成并且 for 循环将被阻塞直到 validateJson() 返回。

    如果validateJson() 中确实有一些异步操作,那么您必须确保validateJson() 返回一个仅在完成所有这些异步操作时才解析的承诺。然后,只有这样,您才能在异步操作运行时使用 await 来“暂停”您的 for 循环。为了帮助您修复validateJson(),我们必须更多地了解什么是异步的,什么不是异步的,以及异步操作有什么接口来知道它们何时完成。然后,我们可以帮助您使 validateJson() 返回一个承诺,该承诺会在正确的时间解决,以使您的 await 正常工作。

    此外,您只能在声明为 async 的函数中使用 await,因此您还必须将其添加到 processJson() 定义中。

    let processJson = async function(items) {
    

    为了说明这个概念,这里有一个简单的例子,你可以在 sn-p 中运行,看看它是如何暂停 for 循环的:

    function delay(t) {
        return new Promise(function(resolve) {
            setTimeout(resolve, t);
        });
    }
    
    async function run() {
        console.log("starting...");
        for (let i = 0; i < 10; i++) {
            await delay(1000);
            console.log("Timer " + i + " fired");
        }
        console.log("done");
    }
    
    run();

    既然你已经添加了更多代码,我们可以谈谈你的真实代码(虽然目前还不清楚resCallback()processItem() 是做什么的,所以这可能还没有结束更改。

    首先更改 maintenanceCall() 以返回一个承诺,我将主要通过切换到 request-promise 模块并返回该承诺来完成:

    const rp = require('request-promise');
    
    const maintenanceCall= function (id, jsonObj, specialFlag) {
            return rp.post({
                url: 'https://url.poster/something',
                auth: {
                    'user': 'user',
                    'pass': 'pass',
                    'sendImmediately': true
                },
                json: true,
                body: {
                    "Input": {
                        "InputParameters": {
                            "KEY": jsonObj.key1,
                            "Hole": jsonObj.Key2,
                            "SomeWhere": jsonObj.keySpecial
                        }
                    }
                }
            }).then(function(body) {
                if (body.OutputParameters.X_MSG_DATA !== null) {
                    resCallback(id + , Message: "
                        + body.OutputParameters.DATA);
                }
                else {
                    // FIX THIS: You define this function here, but never use it, that's odd
                    const sampCheck = function(smsFlag){
                        if(flag=== "Y")
                            return ".X";
                        else if(flag=== "N")
                            return "";
                        else
                            resCallback(id + ": this item can not be processed");
                        processItem(id, jsonObj, stats);
                    }
                }
            });
    };
    

    现在maintenanceCall() 返回了一个承诺,你可以像这样在validateJson() 中使用它,所以它总是返回一个承诺:

    const validateJson = function (id, jsonObj) {
        const processItemSchema = {
            "properties": {
                "key1": {
                    "type": "string"
                },
                "key2": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 3
                },
                "keySpecial": {
                    "type": "string",
                    "minLength": 4,
                    "maxLength": 4
                }
            }
        };
        const ajv = new Ajv();
        let validate = ajv.compile(processItemSchema);
        let valid = validate(jsonObj);
        if (!valid) {
            resCallback(id + ": invalid JSON");
            return Promise.reject(new Error(id + ": invalid JSON"));
        } else {
            // Generate Special Flag(s) value, Comma Separated Value
            let specialFlag = "";
            specialFlag += specialCheck1(jsonObj.keySpecial);
            if(specialFlag.length > 0) {
                let temp = specialCheck2(jsonObj.keySpecial);
                if (temp.length > 0) {
                    specialCheck += "," + temp;
                }
            } else {
                specialFlag += specialCheck1(jsonObj.keySpecial);
            }
            return maintenanceCall(id, jsonObj, specialFlag);
        }
    };
    

    然后,您可以返回到您的 processJson() 函数并让您的 for 循环像这样连续运行:

    let processJson = async function(items) {
        for (const p in items) {
            let newObj = {
                "key1": items[p].key1,
                "key2": items[p].key2,
                "keySpecial": items[p].key3 + items[p].key4
            };
            await validateJson(p, newObj);
        }
    };
    

    而且,调用者可以像这样使用processJson()

    processJson(someItems).then(() => {
       console.log("all done here");
    }).catch(err => {
       console.log(err);
    });
    

    【讨论】:

    • @gray - 由于您添加了更多代码,因此我在解决方案中添加了更多代码。
    • @gray - 我不知道“不再捕获输入”是什么意思?您尚未包含所有代码,因此我已对您显示的代码进行了适当的修改。您可能会遇到错误。您是否记录了所有错误并在调用processJson() 时使用.catch()?但坦率地说,我不知道那条评论是什么意思,所以我对此无能为力。我已经投入了很多精力来帮助您,但是您没有清楚地描述问题并且没有包含所有相关代码,这使得它变得非常困难。
    • @gray - 好吧,我建议的代码中没有console.log('statusCode:', response &amp;&amp; response.statusCode),因为我建议的代码中没有response 变量(我正在使用不使用的请求承诺库该参数,它会自动为您检查状态代码并将非 200 响应转换为被拒绝的承诺)所以我不知道您要运行什么代码。我要去睡觉了。祝你好运。
    • @gray - 是的,这就是它的设计方式。错误停止流动。这可以通过适当的.catch() 处理程序轻松更改。不确定您的要求是什么,或者当出现错误时您希望它做什么,或者哪些错误可以继续处理,哪些错误不可以。
    【解决方案3】:

    您可以使用节点异步循环 https://www.npmjs.com/package/node-async-loop

    var asyncLoop = require('node-async-loop');
     
    var array = ['item0', 'item1', 'item2'];
    asyncLoop(array, function (item, next)
    {
        do.some.action(item, function (err)
        {
            if (err)
            {
                next(err);
                return;
            }
     
            next();
        });
    }, function (err)
    {
        if (err)
        {
            console.error('Error: ' + err.message);
            return;
        }
     
        console.log('Finished!');
    });

    【讨论】:

    • JSON 数据可以是任何类型,如果你的 JSON 数据是数组,那就可以了,试试吧
    【解决方案4】:

    如果您希望代码块运行同步,请使用此 JavaScript 函数:

    // Your loop..
    {
      (function (p) 
      {
          // Your code...
      })(p);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-27
      • 2017-05-19
      • 1970-01-01
      相关资源
      最近更新 更多