2016-09-27 2 views
17

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

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

+0

Некоторые библиотеки обещаний имеют * progress * callbacks. – alex

+0

Невозможно получить это поведение с помощью родного es6? Без добавления другой библиотеки в мой проект? – hackrnaut

+1

Почему не просто 'getStuff (...)' и 'then' render для каждого обещания? –

ответ

18

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

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

Затем создать цепную реакцию, как обрабатывать их (stuff1 перед тем stuff2, stuff2 перед тем stuff3 и т.д.)

// 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) 
 
    });

+1

Ах, это имеет смысл! В принципе, как только я назову getStuff, они будут делать сетевые запросы, а если после этого последуют цепочки, я буду получать поведение, которое я хочу, потому что каждое обещание будет разрешено до того, как. Then вызывается в цепочке? – hackrnaut

+2

Как только вы создадите Promise, он выполнит ваш запрос. Вы можете построить цепочку в любой момент после этого, независимо от того, будет ли обещание разрешено или нет этим пунктом. Если обещание разрешилось до того, как вы создали цепочку, оно просто сохранит свою ценность, пока вы не сделаете цепочку. Если вы сделаете первую цепочку, цепочка будет ждать обещания получить значение. Это красота обещаний. Они всегда решают асинхронно, и вы всегда можете притворяться, будто у вас уже есть значения при кодировании с ними. – nem035

2

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

Bluebird API

6

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 запросов для различных частей данных - есть бэкенд взять диапазоны.

0

Нормальная работа: Вы можете безопасно использовать Promise.all(). Обед исполнителей будет запущен параллельно, и результаты будут возвращены в том порядке, в котором вы вставляете свои обещания в массив обещаний. Тогда вам решать сортировать их так, как вам нравится. Например, в следующем фрагменте мы имеем пять обещаний, каждый из которых будет разрешаться случайным образом в течение 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)));

То, что вы хотите: Но тогда вы не хотите ждать до тех пор, как последний заканчивается, но вместо этого нужно обработать их в том порядке, как можно скорее они разрешаются. Тогда ваш лучший друг здесь Array.prototype.reduce(). Такие, как

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))) 
 
       ]; 
 
promises.reduce((p,c) => p.then(result => (console.log(result + "\n"),c))) 
 
     .then(result => (console.log(result + "\n")));

Пожалуйста запустить код несколько раз, чтобы увидеть, как код ведет себя. Текст будет обновлен как можно скорее, но только в том случае, если это его очередь. Итак, если 1-й решает после 2-го, мы увидим, что 1-й и 2-й появятся сразу в их порядке, но они не будут ждать, пока третий решит так ...

Смежные вопросы