在for循环,Nodejs中异步

问题描述 投票:1回答:4

我希望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。

我怎样才能做到这一点?

这是requireJson函数的请求。

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);
                }
            }
        });
};
json node.js asynchronous request async-await
4个回答
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!');
});

0
投票

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

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

如果validateJson()确实有一些异步操作,那么你必须确保validateJson()返回一个只有在所有这些异步操作完成后才能解析的promise。然后,只有这样,你可以使用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()是做什么的,所以这可能还没有结束变化。

首先更改maintenanceCall()以返回一个承诺,我将主要通过切换到请求承诺模块并返回该承诺:

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()like:

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

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

-1
投票

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

// Your loop..
{
  (function (p) 
  {
      // Your code...
  })(p);
}
© www.soinside.com 2019 - 2024. All rights reserved.