Подтвердить что ты не робот

Выполнять действия как promises выполняться с помощью Promise.all()

Я могу асинхронно разрешить связку promises с Promise.all(array). Однако .then() будет запускаться только после того, как все эти promises будут разрешены. Как я могу выполнять действия при разрешении promises?

Например, я хочу асинхронно загружать все абзацы из статьи с помощью Promise.all(). Таким образом, сеть сразу запрашивает весь огонь. Если в пункте 1 выполняется загрузка, я хочу, чтобы она отображалась на странице, но только если она будет загружена до пункта 2, тогда я хочу загрузить пункт 2. Если абзац 3 сделан, загрузка и 2 нет, я хочу, чтобы 3 дождался 2 перед рендерингом на страницу. И так далее.

Я пробовал что-то вроде этого, но я не знаю, что делать дальше:

var getStuff = function(number, time){
  return new Promise(function(resolve, reject){
    window.setTimeout(function(){resolve(`${number} - Done.`)}, time);
  });
};

Promise.all([ getStuff(1, 200),
              getStuff(2, 100),
              getStuff(3, 250),
              getStuff(4, 200),
              getStuff(5, 300),
              getStuff(6, 250),
              getStuff(7, 5000)])
.then(function(data){
  console.log(data);
});

Как я могу заставить консольный журнал данных происходить один за другим - не разрешая каждое обещание с помощью then() перед тем, как сделать следующий запрос? Есть ли лучший способ сделать это?

4b9b3361

Ответ 1

Вы не можете выполнить этот порядок с помощью Promise.all потому что обещание, возвращаемое из Promise.all ожидает одновременного разрешения всех обещаний из предоставленного массива (в отличие от последовательного), прежде чем само его разрешение.

Вместо этого вы можете индивидуально создавать обещания и запускать их запросы одновременно:

// create promises and make concurrent requests
const s1 = getStuff(1, 200);
const s2 = getStuff(2, 100);
const s3 = getStuff(3, 250);
// ...

Затем создайте цепочку реакций о том, как их обрабатывать (вещи1 перед вещами2, вещи2 перед вещами3 и т.д.)

// create a chain of reaction order to the results of parallel promises
s1
  .then(console.log) // s1 resolved: log result
  .then(() => s2)    // chain s2
  .then(console.log) // s2 resolved: log result
  .then(() => s3)    // chain s3
  // ...
  .then(() => {      // chain another function at at the end for when all promises resolved
    // all promises resolved (all data was logged)
  }

Чтобы реагировать на результаты обещаний в том же порядке, в котором были созданы обещания, вы можете изменить функцию getStuff для динамической цепочки реакций, используя Array.prototype.reduce:

var times = [200, 100, 250, 200, 300, 250, 5000];

var getStuff = function(time, index) { // swap the order of arguments so number is the index passed in from Array.map
  return new Promise((resolve, reject) => {
    window.setTimeout(() => {
      resolve('${index + 1} - Done.'); // use index + 1 because indexes start at 0
    }, time);
  });
};

times
  // map each time to a promise (and number to the index of that time + 1) and fire of a request
  .map(getStuff)
  // dynamically build a reaction chain for the results of promises
  .reduce((chain, promise) => {
    return chain
      .then(() => promise)
      .then(console.log);
  }, Promise.resolve())
  .then(() => {
    // all promises resolved (all data was logged in order)
  });

Ответ 2

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

Вы можете использовать .all для этого с .map:

Promise.all([ getStuff(1, 200),
            getStuff(2, 100),
            getStuff(3, 250),
            getStuff(4, 200),
            getStuff(5, 300),
            getStuff(6, 250),
            getStuff(7, 5000)]
.map(request => request.then(v => {
   console.log("Request done! Got," v); // or some action per request
   return v;
})).then(data => console.log(data));

Вы можете продолжить это с помощью .map, используя тот факт, что вы используете одну и ту же функцию для каждого запроса:

Promise.all([[1, 200],
            [2, 100],
            [3, 250],
            [4, 200],
            [5, 300],
            [6, 250],
            [7, 5000]])
.map((a, b) => getStuff(a, b))
.map(request => request.then(v => {
   console.log("Request done! Got," v); // or some action per request
   return v;
})).then(data => console.log(data));

И далее:

Promise.all([200, 100, 250, 200, 300, 250, 5000])
.map((a, i) => getStuff(a, i + 1))
.map(request => request.then(v => {
   console.log("Request done! Got," v); // or some action per request
   return v;
})).then(data => console.log(data));

Или с синей птицей:

const sideEffect = v => console.log("Got partial result", v));
const data = [200, 100, 250, 200, 300, 250, 5000];
Promise.map(data, (a, i) => getStuff(a, i + 1).tap(sideEffect))
       .then(data => console.log(data));

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

Ответ 3

Я знаю, что он не является родным, но с синей птицей вы можете использовать Promise.some (до полного заполнения после count promises) или Promise.mapSeries (до полного заполнения promises последовательно), чтобы каким-то образом достичь потока вы ожидаете.

Bluebird API

Ответ 4

Нормальная работа: Безопасное использование Promise.all(). Исполнители обещаний будут запущены параллельно, и результаты будут возвращены в том порядке, в котором вы введете свой promises в массив promises. Тогда это зависит от вас, чтобы отсортировать их так, как вам нравится. Например, в следующем фрагменте у нас есть пять promises, каждый из которых будет разрешаться случайным образом в течение 5 секунд. Независимо от времени их разрешения вы получите результаты, когда последние будут решены;

var promises = [ new Promise( v => setTimeout(_ => v("1st paragraph text"),~~(Math.random()*5000))),
                 new Promise( v => setTimeout(_ => v("2nd paragraph text"),~~(Math.random()*5000))),
                 new Promise( v => setTimeout(_ => v("3rd paragraph text"),~~(Math.random()*5000))),
                 new Promise( v => setTimeout(_ => v("4th paragraph text"),~~(Math.random()*5000))),
                 new Promise( v => setTimeout(_ => v("5th paragraph text"),~~(Math.random()*5000))),
               ];
Promise.all(promises)
       .then(result => console.log(result.reduce((p,c) => p + "\n" + c)));