Асинхронный цикл for, Nodejs - PullRequest
       26

Асинхронный цикл for, Nodejs

0 голосов
/ 11 сентября 2018

Я хочу, чтобы цикл 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);
                }
            }
        });
};

Ответы [ 4 ]

0 голосов
/ 11 сентября 2018

Для тех, кто не хочет переписывать и реструктурировать весь свой код.Для тех, кто не хочет ненужных сложностей.Для тех, кто хочет, чтобы элемент в цикле 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
    }
}
0 голосов
/ 11 сентября 2018

Чтобы "приостановить" цикл for с помощью await, вы должны ждать обещания. Таким образом, вы должны заставить validateJson() возвращать обещание, которое разрешается при выполнении любых асинхронных операций внутри этой функции. Вот как async/await работает в Javascript.

Не совсем ясно, что является или не является асинхронным в validateJson(). Если ничто не является асинхронным, то это просто последовательное выполнение, и вам не нужны await или обещания вообще. 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() делают, так что это, возможно, еще не конец изменений.

Сначала измените 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() следующим образом:

processJson(someItems).then(() => {
   console.log("all done here");
}).catch(err => {
   console.log(err);
});
0 голосов
/ 11 сентября 2018

Вы можете использовать нод-асинхронный цикл 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 голосов
/ 11 сентября 2018

Если вы хотите, чтобы ваш кодовый блок выполнял синхронизацию, используйте эту функцию JavaScript:

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