Javascript-форум (https://javascript.ru/forum/)
-   Node.JS (https://javascript.ru/forum/node-js-io-js/)
-   -   Выполнение массива промисов последовательно (https://javascript.ru/forum/node-js-io-js/74312-vypolnenie-massiva-promisov-posledovatelno.html)

arealhz 30.06.2018 17:30

Выполнение массива промисов последовательно
 
Добрый день.
У меня есть промис, который обращается некоторое количество раз к базе данных, производит вычисления и потом при резолве возвращает результат (какую-то структуру). При этом этот промис на входе получает определенную структуру данных из большого массива. Все эти промисы создаются в цикле перебирающем массив входных данных. Сами промисы при создании добавляются в массив. После формирования этого массива я жду Promise.all() и получаю массив результатов. Всё бы хорошо. Но вычисления в зависимости от результатов запросов внутри промиса могут быть разные по протяженности, а количество одновременных подключений к базе данных для решения этих промисов ограниченно. И все прекрасно работает, когда в массиве исходных данных 10-20 элементов. А когда там около 500-1000 элементов, то где-то в середине количество одновременных подключений уже вырастает до недопустимого, и база данных перестаёт быть доступной для большей половины промисов. При этом Promise.all уже не выполняется успешно, так как вылазят ошибки в дочерних промисах.

Теперь внимаение впорос :)

Как можно выполнить массив промисов ПОСЛЕДОВАТЕЛЬНО, то есть что бы элемент n выполнялся строго после завершения выполнения элемента n-1?

P.S.: Так же интересны варианты выполнения массивов промисов секторно, на пример по 10шт за раз (что бы эффективно использовать пул подключений к базе данных на пример)

destus 30.06.2018 19:28

Обычный цикл for с использованием внутри себя await. Если нужно несколько промисов за раз, то соответственно увеличение переменной-счетчик на нужное значение и Promise.all() в теле цикла.

Белый шум 30.06.2018 19:38

По-моему вам нужна обёртка над запросами к БД, которая бы следила за числом подключений к базе.

arealhz 30.06.2018 20:20

Я пробовал вот такую модель:

"use strict";
'esversion: 6';


async function procData(val, i){
	await setTimeout(() => {
		var ret = i + "\t" + val + " #";
        console.log(ret);
      }, 3000);
//	return ret;
}


var arr = [3, 5, "asdf", 8, 0];

//arr.forEach(procData(val,i));

//let i = -5;
for(let n in arr){
	procData(arr[n],n)
//	i++;
}


Я понимаю, что здесь я дожидаюсь исполнения конкретного запроса в промисе, но как это ожидание переместить внутрь цикла формирования промисов не совсем понятно. (

Но ничего не вышло. После долгих лет PERL трудно перейти на асинхронную модель программирования. :(
Может я не правильно реализовал async/await?

arealhz 30.06.2018 20:23

Цитата:

Сообщение от Белый шум (Сообщение 488673)
По-моему вам нужна обёртка над запросами к БД, которая бы следила за числом подключений к базе.

Я "сильно новичок" у меня не тот уровень, что бы писать воркароунд для баз данных. Я постигаю асинхронную модель программирования после долгих лет работы с последовательным выполнением кода. И писал всегда утилиты для администрирования, так как я не программист, а админ :)

Aetae 30.06.2018 20:30

1. setTimeout - не возвращает промис, его бесполезно await.
2. ключевое слово await имеет смысл использовать именно там, где ты что-то ждёшь.(а конкретно исключительно перед промисом или вызовом async функции) Если async функция вызвана без этого ключевого слова - то она просто вызвана, дальнейший код не ждёт асинхронного ответа, независимо от того, что там у ней внутри.

Примерно так должен был выглядеть код:
"use strict";
'esversion: 6';
(async function(){

  function procData(val, i){
    return new Promise((resolve, reject) => setTimeout(() => {
      var ret = i + "\t" + val + " #";
      console.log(ret);
      resolve(ret)
    }, 3000));
  }


  var arr = [3, 5, "asdf", 8, 0];

  for(let n in arr){
    await procData(arr[n],n)
  }

}())

arealhz 30.06.2018 21:04

Цитата:

Сообщение от Aetae (Сообщение 488678)
1. setTimeout - не возвращает промис, его бесполезно await.
2. ключевое слово await имеет смысл использовать именно там, где ты что-то ждёшь.(а конкретно исключительно перед промисом или вызовом async функции) Если async функция вызвана без этого ключевого слова - то она просто вызвана, дальнейший код не ждёт асинхронного ответа, независимо от того, что там у ней внутри.

Примерно так должен был выглядеть код:
"use strict";
'esversion: 6';
(async function(){

  function procData(val, i){
    return new Promise((resolve, reject) => setTimeout(() => {
      var ret = i + "\t" + val + " #";
      console.log(ret);
      resolve(ret)
    }, 3000));
  }


  var arr = [3, 5, "asdf", 8, 0];

  for(let n in arr){
    await procData(arr[n],n)
  }

}())

Это действительно работает, но сам код для меня похож на магию вне Хогвартса. :(

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

Спасибо большое за пример работающей конструкции! Буду теперь RTFM...

Aetae 30.06.2018 21:46

Чтоб в этом разбираться, надо просто понимать, что async и await - это не что-то самостоятельное, а просто удобная обёртка над Promise. Promise же в свою очередь - это не что-то самостоятельное, а просто удобная обёртка над коллбэками.)
Поняв коллбэки поймёшь и остальное.)

Alexandroppolus 01.07.2018 00:45

Цитата:

Сообщение от arealhz (Сообщение 488668)
P.S.: Так же интересны варианты выполнения массивов промисов секторно, на пример по 10шт за раз (что бы эффективно использовать пул подключений к базе данных на пример)

Самый правильный вариант - "общая очередь". Запускаются параллельно 10 исполнителей, каждый из которых последовательно забирает и обрабатывает элементы из очереди и резолвится, если очередь опустела. А внешний Promise.all ждёт всех десятерых.

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

Audaxviator 01.07.2018 04:52

Посмотрите старую добрую библиотеку async, там 100500 методов на все вкусы и случаи жизни.


Часовой пояс GMT +3, время: 18:06.