2015-09-08 2 views
4

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

Будет ли это ужасно сложно реализовать? Или это слишком много, чтобы спросить здесь о том, как его реализовать? Я действительно надеюсь, что нет, поэтому я прошу, если кто-нибудь, возможно, осуществил это раньше, поделиться идеей о том, как это сделать правильно.

Предпосылка для этого заключается в возможности использовать его в транзакции базы данных, которая должна выполнить commit/rollback после завершения вызова, и у него не может быть никаких обещаний, которые все еще пытаются решить за пределами транзакционного вызова.

EDIT: Ссылка, предоставленная на другой вопрос, очень полезна, но это не полный ответ на заданный вопрос. Общий settle - отличный пример, который помог многим, но его необходимо было упростить и объединить в логику all, чтобы соответствовать сценарию транзакций, описанному ранее.

+3

Код, предоставленный именно для того, что вы просите здесь (во втором примере кода с именем 'promSettle()'): [Подождите, пока несколько обещаний будут отклонены] (http://stackoverflow.com/questions/32241391/wait -дль многодисковых обещания-к-быть отвергнуты). Помечен этот вопрос в качестве дубликата. – jfriend00

+0

Это была очень полезная ссылка, хотя и предлагала только половину ответа. –

+0

Какая половина отсутствует. Он выполняет все обещания и рассказывает вам, когда все это делается. – jfriend00

ответ

3

Основываясь на общей функции promiseSettle() от the other question, вы можете сделать это и иметь как общую функцию типа settle(), так и свою более конкретную версию в качестве обертки вокруг нее. Это даст вам общую способность делать много .settle() типов поведения и иметь свой собственный специфический аромат, а также построить другие специфические ароматы по мере необходимости:

Итак, вот общий promiseSettle(), который возвращает вам состояние всех обещаний и решает только тогда, когда все принятые в обещании сделано:

function promiseSettle(promises) { 
    return new Promise(function(resolve) { 
     var remaining = promises.length; 
     // place to store results in original order 
     var results = new Array(remaining); 

     function checkDone() { 
      if (--remaining === 0) { 
       resolve(results); 
      } 
     } 

     promises.forEach(function(item, index) { 
      // check if the array entry is actually a thenable 
      if (typeof item.then === "function") { 
       item.then(function(value) { 
        // success 
        results[index] = {state: "fulfilled", value: value}; 
        checkDone(); 
       }, function(err) { 
        // reject error 
        results[index] = {state: "rejected", value: err}; 
        checkDone(); 
       }); 
      } else { 
       // not a thenable, just return the item itself 
       results[index] = {state: "fulfilled", value: item} 
       --remaining; 
      } 
     }); 
     // special case for zero promises passed 
     if (remaining === 0) { 
      resolve(results); 
     } 
    }); 
} 

И вот обертка на нем, что дает Вам определенное поведение:

// Either fulfills with an array of results or 
// rejects with the first error, but it does not do either 
// until all promises have completed which makes it different than 
// promise.all() 
function promiseSettleAll(promises) { 
    return promiseSettle(promises).then(function(results) { 
     for (var i = 0; i < results.length; i++) { 
      if (results[i].state !== "fulfilled") { 
       // reject with the first error found 
       throw results[i].value; 
      } 
     } 
     // all were successful, return just array of values 
     return results.map(function(item) {return item.value;}); 
    }); 
} 
+0

Упрощенный 'promSettleAll()' с помощью простого цикла 'for'. – jfriend00

+0

После тщательного рассмотрения я решил также отслеживать все отклонения. Кажется, это хорошая идея :) –

+0

@ vitaly-t - Интересно. Что привело к смене разума? – jfriend00

0

В конце всех исследований, написания, тестирования и оптимизации он был превращен в библиотеку (spex), которая фокусируется на таких вещах.

В частности, метод batch - это тот, который реализует слияние описанной логики.

Я не переиздаю its source code здесь, потому что он теперь делает a lot more, чем изначально искал в вопросе.

+0

Какова цель этого кода, потому что непонятно, что вы пытаетесь сделать здесь, и, видимо, я не понимал, что вы пытаетесь сделать в своем вопросе. В моем коде вы можете просто запустить 'promSettle()', а затем перечислить возвращаемые результаты, чтобы увидеть, что удалось, и что не удалось. То есть, в конце концов, что делает '.settle()' Bluebird ', что я и думал, что вы просили. – jfriend00

+0

Сначала нужно решить все обещания в массиве, делая это таким образом, который необходим в контексте операции «все», которая его содержит, а затем возвращает результат с той же логикой, что и «все». –

+0

Но что вы делаете, когда обещание отвергает. Как вы общаетесь с этим. Вы не можете возвращать результаты так же, как 'Promise.all()', потому что вам нужно сообщить, какие из них были успешными, а какие - неудачны. – jfriend00

7

Я думаю, что решение по jfriend чрезмерно потому что он строится на вершине settle, он запускает семафор и делает много странных вещей вместо использования встроенных примитивов, таких как .all.

Вместо этого, если мы строим на Bluebird новее reflect примитив (его реализация в родных обещаниях), мы можем получить более чистый API и реализацию:

function reflect(promise){ 
    return promise.then(x => ({state: "fulfilled", value: x}), // arrows, assume nodejs 
         e => ({state: "rejected" , value: e})); 
} 

На вершине отражение, мы можем построить другие примитивы легко :

function settle(promises){ 
    return Promise.all(promises.map(reflect)); // much cleaner 
} 

Если мы хотим подождать, а затем разрешить/отклонить на основе значений это просто:

function allWait(promises){ 
    return settle(promises).then(results => { 
     var firstFailed = results.find(r => r.state === "rejected"); 
     if(firstFailed) throw firstFailed.value; 
     return results; 
    }); 
} 
+0

Разве вы не отклонили бы здесь массив? – Bergi

+0

@Bergi Я хотел показать правильный способ написать тот же код. Лично я бы не занимался такими транзакциями, как это начиналось с: я бы использовал утилиты и шаблон удаления, как это делают ORM. –

+0

В этом вопросе четко указано, что в нескольких местах указано в комментариях, что мне нужно решение на основе стандартных обещаний, а не одна конкретная библиотека обещаний. Я не могу использовать «поселение» Блюберда или что-то в этом роде. Решение jfriend00 - это то, что работает для меня. –

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