Форум программистов, компьютерный форум, киберфорум
JavaScript для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.83/6: Рейтинг темы: голосов - 6, средняя оценка - 4.83
3 / 3 / 1
Регистрация: 20.02.2018
Сообщений: 126

Асинхронность и цикл

25.06.2019, 18:23. Показов 1121. Ответов 2
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте!

Не могу понять как правильно сделать перебор всех страниц сайта и у каждой странице сделать свою выборку?Чет я запутался. Я хочу с 30 страниц собрать тайтлы статей, как это можно сделать? Как совместить цикл и асинхронные запросы? В данном случае он выберет, но консоль лог выведет результат через 4сек.

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let itemList = [];
let countPages = 30;
 
        while(countPages >= 0){
 
            unirest
            .get('https://siite.ru/&page=' + countPages)
            .then((res) => {
                const body = res.body;
                const $ = cheerio.load(body);
                   
                $('.bloko-link').each(function(i, el) {
                  itemList.push($(this).text());
                });
            });
 
            countPages--;
        } 
 
        setTimeout(()=>{
            console.log(itemList);
        }, 4000)
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
25.06.2019, 18:23
Ответы с готовыми решениями:

Создать программу по всем 3 видам циклов...цикл с параметром,цикл с условием,цикл,и цикл с предусловием...
Найти сумму чисел 1 в квадрате до 10 c квадрате...операцию возведению в степень не использовать...

Асинхронность
Добрый день, у меня такая проблема. Создал поток, запустил его и программа заглючивает, но...

Асинхронность?
Уже как то создавал подобную тему, но моя текущая реализация меня не устраивает чуть более чем...

2
Эксперт JS
6496 / 3907 / 2006
Регистрация: 14.06.2018
Сообщений: 6,781
25.06.2019, 20:35
Здравствуйте!
Для основной страницы index.html и трех страниц с заголовками, расположенных в той же папке и на том же сервере:
index.html
PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <script src="tap.js"></script>
    <style>
        #resultsTextBox {
            width: 200px;
            height: 200px;
            border: 1px solid black;
        }
 
        input[type=button] {
            width: 99px;
        }
    </style>
</head>
<body>
    <div id="resultsTextBox"></div>
    <input type="button" id="startButton" value="Start" />
    <input type="button" id="cancelButton" value="Cancel" />
    <script>
        let startButton = document.getElementById("startButton"),
            cancelButton = document.getElementById("cancelButton"),
            resultsTextBox = document.getElementById("resultsTextBox");
 
        let cts; // Токен отмены
 
        let itemList = []; // Список названий статей
 
        startButton.onclick = startButton_Click;
        cancelButton.onclick = cancelButton_Click;
 
        // Терминальная ловушка асинхронности
        async function startButton_Click() {
            resultsTextBox.innerHTML = "";
 
            // Instantiate the CancellationTokenSource.
            cts = new CancellationTokenSource();
 
            try {
                await AccessTheWebAsync(cts.token);
                resultsTextBox.innerHTML += "<br />Обработка успешно завершена.";
                for (let item of itemList)
                    resultsTextBox.innerHTML += `<br />${item}`;
            }
            catch (e) {
                if (e.message === "Operation canceled")
                    resultsTextBox.innerHTML += "<br />Обработка отменена.";
                else
                    resultsTextBox.innerHTML += "<br />Ошибка обработки.";
            }
 
            cts = null;
        }
 
        function cancelButton_Click() {
            if (cts) {
                cts.cancel();
            }
        }
 
        async function AccessTheWebAsync(/*CancellationToken*/ ct) {
            // Список адресов
            let urlList = SetUpURLList();
 
            // Список горячих задач, которые запустятся все одновременно
            let /*IEnumerable<Task>*/ downloadTasks =
                urlList.map(url => ProcessURL(url, ct));
 
            for (let t of downloadTasks) {
                Process(await t);
            }
        }
 
        function Process(num) {
            resultsTextBox.innerHTML += `<br />Добавлено названий:  ${num}`;
        }
 
        function /*string[]*/ SetUpURLList() {
            let countPages = 3,
                urls = [];
            while (countPages > 0)
                urls.push(`/page${(countPages--)}.html`);
            return urls;
        }
 
        async function /*Promise<int>*/ ProcessURL(/*string*/ url, /*CancellationToken*/ ct = CancellationToken.none) {
            // Если отмена, то не надо и начинать общение с сервером
            ct.throwIfCancellationRequested();
            // Загрузить текст страницы
            let urlContents = await getTextAsync(url, ct);
            let fragment = document.createElement("html");
            fragment.innerHTML = urlContents;
            let array = fragment.querySelectorAll(".bloko-link");
            array.forEach(e => itemList.push(e.textContent));
            return array.length;
        }
    </script>
</body>
</html>
tap.js
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
class CancellationToken {
    constructor(isCancellationRequested) {
        this.isCancellationRequested = isCancellationRequested;
    }
    throwIfCancellationRequested() {
        if (this.isCancellationRequested)
            throw new Error("Operation canceled");
    }
    static get none() {
        if (!CancellationToken._n)
            CancellationToken._n = new CancellationToken(false);
        return CancellationToken._n;
    }
}
class CancellationTokenSource {
    constructor() {
        this._t = new CancellationToken(false);
    }
    get token() { return this._t; }
    cancel() { this._t.isCancellationRequested = true; }
    cancelAfter(ms) { setTimeout(this.cancel.bind(this), ms); }
}
 
/*
 * Task.Delay()
 */
Promise.delay = ms => new Promise(resolve => { setTimeout(resolve, ms); });
 
/**
 * Task.Delay(ms, cancellationToken)
 * Для упрощения проверяет отмену каждую секунду
 */
Promise.delay2 = (ms, cancellationToken = CancellationToken.none) => new Promise((resolve, reject) => {
    if (cancellationToken.isCancellationRequested)
        reject(new Error("Operation canceled"));
 
    let i = 0,
        t = Math.floor(ms / 1000),
        t2 = ms % 1000,
        id;
    if (t)
        id = setInterval(func, 1000);
    else
        setTimeout(resolve, t2);
 
    function func() {
        if (cancellationToken.isCancellationRequested) {
            clearInterval(id);
            reject(new Error("Operation canceled"));
        }
        else if (++i >= t) {
            clearInterval(id);
            if (t2)
                setTimeout(resolve, t2);
            else
                resolve();
        }
    }
});
 
/*
* Статический метод, обеспечивающий механизм "правильной" асинхронности с обработкой ошибок.
* Действительно сначала возвращает промис в состоянии ожидания,
* и действительно запускает функцию обратного вызова в асинхронном режиме.
*/
Promise.run = callback => new Promise((resolve, reject) => {
    Promise.resolve().then(() => {
        try {
            let result = callback();
            resolve(result);
        }
        catch (e) {
            reject(e);
        }
    });
});
 
/**
 * Возвращаемый промис будет завершен, когда любой из последовательности промисов завершен. 
 * Возвращаемый промис всегда будет завершаться в состоянии resolved. 
 * Это справедливо, даже если первый завершенный промис находится в состоянии rejected.
 * 
 * Поскольку в JavaScript результат промиса не может быть промисом, то 
 * результат возвращенного промиса — массив из одного элемента: первого завершенного промиса.
 */
Promise.whenAny = promises => new Promise((resolve, reject) => {
    let result;
    for (let promise of promises) {
        if (result) break;
        let func = () => { if (!result) { result = [promise]; resolve(result); } };
        promise.then(func, func);
    }
});
 
/**
 * TAP-версия загрузки текста по HTTP методом GET с возможностью отмены.
 * @param {string} url Строка URL.
 * @param {CancellationToken} ct Токен отмены.
 */
async function /*Promise<string>*/ getTextAsync(url, /*CancellationToken*/ ct = CancellationToken.none) {
    let response = await fetch(url);
    ct.throwIfCancellationRequested();
    if (response.ok) {
        let text = await response.text();
        ct.throwIfCancellationRequested();
        return text;
    }
    else throw new Error(`${response.status}: ${response.statusText}`);
}
/**
 * TAP-версия загрузки json по HTTP методом GET с возможностью отмены.
 * @param {string} url Строка URL.
 * @param {CancellationToken} ct Токен отмены.
 */
async function /*Promise<json>*/ getJSONAsync(url, /*CancellationToken*/ ct = CancellationToken.none) {
    let response = await fetch(url);
    ct.throwIfCancellationRequested();
    if (response.ok) {
        let json = await response.json();
        ct.throwIfCancellationRequested();
        return json;
    }
    else throw new Error(`${response.status}: ${response.statusText}`);
}
/**
 * TAP-версия загрузки blob по HTTP методом GET с возможностью отмены.
 * @param {string} url Строка URL.
 * @param {CancellationToken} ct Токен отмены.
 */
async function /*Promise<blob>*/ getBLOBAsync(url, /*CancellationToken*/ ct = CancellationToken.none) {
    let response = await fetch(url);
    ct.throwIfCancellationRequested();
    if (response.ok) {
        let blob = await response.blob();
        ct.throwIfCancellationRequested();
        return blob;
    }
    else throw new Error(`${response.status}: ${response.statusText}`);
}
Справка Настройка асинхронного приложения (C#)

Добавлено через 3 минуты
Пример page1.html
PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
</head>
<body>
    <h1 class="bloko-link">page1-1</h1>
    <h1 class="bloko-link">page1-2</h1>
    <h1 class="bloko-link">page1-3</h1>
    <h1 class="bloko-link">page1-4</h1>
</body>
</html>
1
8 / 5 / 3
Регистрация: 30.04.2019
Сообщений: 24
26.06.2019, 03:30
riddlejs, здравствуйте.

Одна часть вашей программы выполнится «сейчас», а другая -- «позже». Считайте, что выполняются они последовательно и между ними есть какой-то временной интервал. Следовательно, в «сейчас» части вам недоступны данные, которые только появятся в «позже» части.

В вашем примере, в «сейчас» части находится весь код кроме стрелочной функция в методе .then (c 9 по 15 строку). Она выполнится в «позже» части когда будут готовы данные. До тех пор, ваш массив itemList будет пустым.

Когда вы решили использовать setTimeout, вы еще раз разбили выполнения своей программы, добавив еще одну «позже» часть (анонимная стрелочная функция с 20 по 22 строки). Благодаря задержке, подобранной случайно, эта новая «позже» часть выполнилась позднее всех и поэтому массив уже заполнен данными к этому моменту.

В этом и есть суть и боль асинхронности.

Вы делаете 30 запросов, каждый создает свою «позже» часть, для которой имеется обработчик в виде стрелочной функции. В ней вы достаете заголовок страницы и по помещаете его в доступный через замыкание, причем для каждого обработчика, массив itemList.

unirest.get() возвращает promise. Вам можно создать функцию getPages(), в которой вы создадите массив из 30 промисов и воспользуетесь Promise.all() чтобы получить массив страниц для дальнейшего парсинга.

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
const getPages = () => {
    let countPages = 30,
        pagePromises = [];
 
    while(countPages >= 0){
    pagePromises.push(
        unirest.get('https://siite.ru/&page=' + countPages)
    );
    countPages--;
     }
    return Promise.all(pagePromises);
}
 
// Вариант 1
getPages()
    .then(pages => {
        let titles = pages.map(page => {
        // получаете заголовки
    });
 
       /*
            "позже" часть Вашей программы.
            Если нужно еще что-нибудь запрашивать,
            то делайте прямо сдесь и возвращайте
            промиси через return. А дальше сколько 
            угодно делать:
 
                  .then(newData1 => {
 
                   })
                  .then(newData2 => {
 
                   })
        */
}); 
 
// Вариант 2
async function parseTitles() {
        /*
             Здесь код будет выглядеть синхронно,
             хотя и будет выполняться асинхронно
       */
 
     try {
         let pages = await getPages();
         // получаете заголовки
     }
     catch (err) {
       /*
            Помните, при использовании Promise.all, если хоть одна
            reject-нится, Вы попадете сюда и никаких заголовков не
            получите
       */
       }
}
 
parseTitles();
2
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
26.06.2019, 03:30
Помогаю со студенческими работами здесь

Асинхронность в js
почему в консоле не находит переменную obj, но когда убираю setTimout, только тогда находит ...

Асинхронность
Всем привет) Я новичек в ноде, разрабатываю веб сервер. this.tokens.get(token, function(msg,...

асинхронность серверов
подскажите пожалуйста,где можно почитать про асинхронность серверов, или будьте добры расскажите...

Асинхронность в VS2010
Возможно ли использование асинхронности в VS2010, async &amp; wait В одной статье написано что...

Потоки и асинхронность
Если есть сервер то для каждого клиента было бы логично создавать отдельный поток,но проблема в том...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
3
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
SDL3 для Android: Загрузка PNG с альфа-каналом с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 28.01.2026
Содержание блога SDL3 имеет собственные средства для загрузки и отображения PNG-файлов с альфа-каналом и базовой работы с ними. В этой инструкции используется функция SDL_LoadPNG(), которая. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru