Ожидайте загрузки файла, затем запускайте функцию в каждой «строке», но ожидайте возврата в каждой строке - PullRequest
0 голосов
/ 02 июля 2019

Я пытался понять Обещания, и я бью кирпичную стену.

== Заказать Я хочу, чтобы код работал ==

  1. Мне нужен файл .txt для загрузки каждой строки в массив.
  2. ПОДОЖДИТЕ, чтобы это произошло.
  3. Запуск функции для каждой возвращаемой записи и массива.
  4. ПОДОЖДИТЕ для каждого индекса массива, который будет обработан перед выполнением следующего.

== Мои функции ==

  1. Вызовите эту функцию, чтобы запустить программу.
async function start(){
    var data = await getData();
    console.log(data);
    for (var i = 0; i < data.length; i++){
        console.log(await searchGoogle(data[i]));
    }

}
  1. 'ожидайте' данных из getData
async function getData(){
    return new Promise(function(resolve, reject){
        fs.readFile('./thingsToGoogle.txt', function(err, data) {
            if(err) throw err;
            var array = data.toString().split("\n");
            resolve(array); 
        });
    });
}
  1. Затем вызовите searchGoogle для каждого индекса в массиве.
async function searchGoogle(toSearch) {
    (async() => {
        const browser = await puppeteer.launch();
        const page = await browser.newPage();
        await page.goto('https://www.google.com/');
        await page.type('input[name=q]', toSearch);
        try {
            console.log('Setting Search' + toSearch);           
            await page.evaluate(() => {
                let elements = document.getElementsByClassName('gNO89b');
                for (let element of elements)
                    element.click();
            });
            await page.waitForNavigation();         
        } catch (err) {
            console.log(err)
        }

        try {
            console.log("Collecting Data");
            const[response] = await Promise.all([
                        page.waitForNavigation(),
                        await page.click('.rINcab'),
                    ]);
        } catch (err) {
            console.log("Error2: " + err)
        }

        let test = await page.$$('.LC20lb');
        // console.log(test);
        allresults = [];
        for (const t of test) {
            const label = await page.evaluate(el => el.innerText, t);
            if (label != "") {
                allresults.push(label);
            }
        }
        await browser.close();
        resolve(allresults);

    })();
}

Проблема в том, что это не работает. он не ждет загрузки файла.

Изображение выхода Node JS.

Надеюсь, снимок экрана был загружен, но вы можете видеть, что он складывает функцию SearchGoogle console.logs;

console.log('Setting..')
console.log('Setting..')
console.log('Collecting..')
console.log('Collecting..')

Когда это должно быть

console.log('Setting..')
console.log('Collecting..')
console.log('Setting..')
console.log('Collecting..')

Это первый раз, когда мы имеем дело с обещаниями, я много читал о них и делал кусочки кода, чтобы понять их, однако, когда я пытался применить эти знания, я боролся. Надеюсь, что кто-то может помочь.

-Peachman-

Ответы [ 2 ]

1 голос
/ 04 июля 2019

Очередь с одновременным лимитом (с использованием p-queue)

Вам нужна очередь с лимитом одновременности.Вы будете читать каждую строку и добавлять их в очередь.Для этого мы будем использовать readline и p-queue .

Сначала создайте очередь с параллелизмом 1.

const {default: PQueue} = require('p-queue');
const queue = new PQueue({concurrency: 1});

Затем создайте наш экземпляр для чтения.

const fs = require('fs');
const readline = require('readline');

const rl = readline.createInterface({
  input: fs.createReadStream('your-input-file.txt')
});

Для каждой строки файла добавьте запись в очередь.

rl.on('line', (line) => {
  console.log(`Line from file: ${line}`);
  queue.add(() => searchGoogle(line));
});

Вот и все!Если вы хотите обработать 10 строк одновременно, просто измените строку параллелизма.Он по-прежнему будет читать по одной строке за раз, но очередь будет ограничивать количество вызовов searchGoogle.

enter image description here

Необязательные исправления: Async Await

Ваш код имеет следующую структуру:

async yourFunction(){
  (async()=>{
    const browser = await puppeteer.launch();
    // ... rest of the code
  })()
}

Хотя это может работать как задумано, вам будет сложно отлаживать, потому что вы будете создавать анонимную функцию каждый раз, когда запускаете yourFunction.

Следующего достаточно.

async yourFunction(){
    const browser = await puppeteer.launch();
    // ... rest of the code
}
0 голосов
/ 04 июля 2019

Вот способ их обработки, который позволяет обрабатывать N URL одновременно, когда вы можете настроить значение N. Я предполагаю, что вы хотите, чтобы оно было установлено в диапазоне от 5 до 20, чтобы ваш процессор был занят , но не используйте слишком много ресурсов сервера.

Вот схема того, как это работает:

  1. Он использует построчный модуль для построчного чтения файла и (в отличие от встроенного интерфейса readline) этот модуль приостанавливает события line при вызове .pause(), что важно в этой реализации.
  2. Он поддерживает счетчик numInFlight, который сообщает вам, сколько строк находится в процессе обработки.
  3. Вы устанавливаете константу maxInFlight равной максимальному количеству строк, которые вы хотите обрабатывать параллельно.
  4. Он поддерживает resultCntr, который помогает вам сохранять результаты в правильном порядке.
  5. Создает интерфейс readline и устанавливает прослушиватель для события line. Это запустит поток с line событиями.
  6. Для каждого события line мы увеличиваем наш счетчик numInFlight. Если мы достигли максимально допустимого числа в полете, мы приостанавливаем поток readline, чтобы он больше не генерировал line событий. Если мы еще не достигли максимума в полете, то больше событий line будут проходить, пока мы не достигнем максимума.
  7. Мы передаем эту строку существующей функции searchGoogle().
  8. Когда эта строка завершит обработку, мы сохраняем результат в соответствующем месте в массиве, уменьшаем счетчик numInFlight и возобновляем поток (если он был ранее приостановлен).
  9. Мы проверяем, все ли мы закончили (проверяя, является ли numInFlight 0 и достигли ли мы конца нашего файла). Если мы закончили, разрешите мастер-обещание с результатами.
  10. Если мы еще не все закончили, то будет либо больше line событий, либо больше функций searchGoogle() в полете, которые завершатся, обе из них еще раз проверит, чтобы убедиться, что мы закончили.
  11. Обратите внимание, что способ, с помощью которого это работает, заключается в том, что ошибки в любом данном URL-адресе просто помещаются в массив результатов (объект ошибки находится в массиве), а обработка остальных URL-адресов продолжается с возможным разрешенным обещанием. Ошибки при чтении входного файла прекратят обработку и отклонят обещание возврата.

Вот код:

const fs = require('fs');
const Readline = require('line-by-line');

function searchAll(file) {
    return new Promise(function(resolve, reject) {
        const rl = new Readline(file);
        // set maxInFlight to something between 5 and 20 to optimize performance by
        // running multiple requests in flight at the same time without
        // overusing memory and other system resources.
        const maxInFlight = 1;

        let numInFlight = 0;
        let resultCntr = 0;
        let results = [];
        let doneReading = false;

        function checkDone(e) {
            if (e) {
                reject(e);
            } else if (doneReading && numInFlight === 0) {
                resolve(results);
            }
        }

        rl.on('line', async (url) => {
            if (url) {
                let resultIndex = resultCntr++;
                try {
                    ++numInFlight;
                    if (numInFlight >= maxInFlight) {
                        // stop flowing line events when we hit maxInFlight
                        rl.pause();
                    }
                    let result = await searchGoogle(url);
                    // store results in order
                    results[resultIndex] = result;
                } catch(e) {
                    // store error object as result
                    results[resultIndex] = e;
                } finally {
                    --numInFlight;
                    rl.resume();
                    checkDone();
                }
            }
        }).on('end', () => {
            // all done reading here, may still be some processing in flight
            doneReading = true;
            checkDone();
        }).on('error', (e) => {
            doneReading = true;
            checkDone(e);
        });    
    });
}

К вашему сведению, вы можете установить для maxInFlight значение 1, и он будет считывать URL-адреса, обрабатываемые по одному, но весь смысл написания этого типа функции заключается в том, что вы, вероятно, сможете повысить производительность, установив его на значение выше 1 (я предполагаю 5-20).

...