2016-12-21 4 views
0

С Array of Пообещано как подождать, пока все они не будут решены, прежде чем продолжить?Ожидание обещаний разрешить в пределах массива

Ниже приведен пример реализации, где task_2 уволяет его отказ до того, как task_1 может запускаться.

const $output = $$('output') 
 

 
const config = { 
 
    task_1: [ 
 
    val => new Promise((resolve, reject) => setTimeout(() => reject('Awaited error'), 2000)), 
 
    val => new Promise((resolve, reject) => resolve(val)) 
 
    ], 
 
    task_2: [ 
 
    val => new Promise((resolve, reject) => resolve(val)), 
 
    val => new Promise((resolve, reject) => reject('An error')), 
 
    ] 
 
} 
 

 
taskRunner({ 
 
    task_1: 'task parameters', 
 
    task_2: 'task parameters' 
 
    }) 
 
    .then(res => $output.text(res).fadeIn()) 
 
    .catch(err => $output.text(err).fadeIn()) 
 

 
function taskRunner(tasks) { 
 
    return new Promise((resolve, reject) => { 
 
    let arr = [] 
 
    for (const task in tasks) { 
 
     if (!config.hasOwnProperty(task)) return reject(`${task} has no tasks`) 
 
     arr.push(config[task].map((cb => cb(tasks[task])))) 
 
    } 
 

 
    const toRun = arr.reduce((a, b) => a.concat(b)) 
 
    Promise.all(toRun) 
 
     .then(res => resolve(res)) 
 
     .catch(err => reject(err)) 
 
    }) 
 
} 
 

 
function $$(data) { 
 
    return $('[data-' + data + ']') 
 
}
html, body { margin: 0; padding: 0 } 
 
div.output { 
 
    font-family: 'Courier', monospace; 
 
    color: #383838; 
 
    margin: 15px; 
 
    padding: 15px 20px; 
 
    background: #fafafa; 
 
    border-radius: 3px; 
 
    border-bottom: 1px solid #dadada; 
 
} 
 
.js-hide { display: none }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<div class="output js-hide" data-output></div>

+1

Какой вопрос? Что не ведет себя так, как ожидалось? Нет необходимости создавать новое обещание в вашем taskrunner, вы можете просто «вернуть Promise.all (..)» –

+0

Согласно docs Promise All отклоняется, если какой-либо из элементов отклонен: https: // developer. Mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all –

+0

@DanielB Первая задача сначала не отбрасывается, во-вторых, как я могу обработать отказ от задачи, не существующей в 'config', просто Возвращая 'prom.all()'? – MindVox

ответ

0

Если я правильно понимаю ваш вопрос от вашего комментария

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

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

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

Существует .always() функции JQuery, которые работают, как это, и если бы вы использовали BlueBird вы могли бы использовать .reflect() для проверки обещаний Promise.all(). В ES6 нет текущей функции, которая может это сделать.

Заимствование из ответа на этот вопрос, ES6 promise settled callback?, вы можете реализовать свою собственную версию .always()/.finally()/.allSettled() как этот

Promise.prototype.finally = function(cb) { 
    const res =() => this 
    return this.then(value => 
     Promise.resolve(cb({state:"fulfilled", value})).then(res) 
    , reason => 
     Promise.resolve(cb({state:"rejected", reason})).then(res) 
    ); 
}; 
0

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

В качестве примера;

function handleRejectedPromises(p){ 
 
    return new Promise((resolve,reject) => p.then(v => resolve({status: "resolved", value: v})) 
 
              .catch(x => resolve({status: "rejected", reason: x}))); 
 
} 
 
var ps = [Promise.resolve(42), Promise.reject(37), Promise.resolve(23), Promise.resolve(11)]; 
 

 
Promise.all(ps.map(p => handleRejectedPromises(p))) 
 
     .then(ra => ra.forEach(r => console.log(r)));

OK согласно @ комментарий Берги, я должен исправить свой ответ, соответственно, для того, чтобы принять его анти-паттерна области.

function handleRejectedPromises(p){ 
 
    return p.then(v => ({status: "resolved", value: v})) 
 
      .catch(x => ({status: "rejected", reason: x})); 
 
} 
 
var ps = [Promise.resolve(42), Promise.reject(37), Promise.resolve(23), Promise.resolve(11)]; 
 

 
Promise.all(ps.map(p => handleRejectedPromises(p))) 
 
     .then(ra => ra.forEach(r => console.log(r)));

+0

Избегайте антипаттера конструктора '' Promise' (http://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi

+0

@ Bergi Я согласен, я тоже не люблю проводить обещание строительства внутри обещаний ... Нет, я не думаю, что это анти-шаблон в соответствии с этой темой. Я считаю, что именно эта задача должна быть выполнена в пределах доступных границ местных обещаний JS. Иногда есть вещи, которые нужно изобретать, которые могут показаться неортодоксальными, как инвертирование разрешения и отклонение в 'Promise.all()' для того, чтобы получить первое решение обещать, не попадая на отклоняющие. Это всего лишь один из них. – Redu

+1

Это определенно антипаттерн. Вся эта новая конструкция Promise должна быть просто заменена эквивалентом (без ваших ошибок) и намного проще 'p.then (v => ({status:« resolved », value: v}), x => ({status : "отклонен", причина: х})) '. – Bergi