异步for循环,Node.js

时间:2018-09-11 03:02:56

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

我希望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);
    }
};

在进入循环中的下一个p之前,我需要validateJson完成其异步工作链。

我该怎么做?

这是请求的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);
                }
            }
        });
};

4 个答案:

答案 0 :(得分:0)

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

目前尚不清楚validateJson()中什么是异步的,哪些不是异步的。如果没有什么是异步的,那么它只是串行执行,您根本不需要await或promises。 Javascript是单线程的,因此它将只运行validateJson()直到完成,并且for循环将被阻塞直到validateJson()返回。

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

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

let processJson = async function(items) {

为了说明这个概念,下面是一个简单的示例,您可以在代码段中的此处运行以查看其如何暂停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()所做的事情,因此这可能还没有结束。 / p>

首先更改maintenanceCall()以返回一个Promise,我将主要通过切换到请求承诺模块并返回该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);
});

答案 1 :(得分:0)

您可以使用node-async-loop 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!');
});

答案 2 :(得分:0)

对于那些不想重写和重组所有代码的人。对于那些不想不必要的复杂性的人。对于那些希望其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
    }
}

答案 3 :(得分:-1)

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

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