Как дождаться выполнения нескольких Promise аналогично Promise.all

258
22 сентября 2017, 21:57

Promise.all возвращает промис, который отклоняется при отклонении любого из переданных all обещаний. Например следующий код:

const a = new Promise((resolve, reject) => { 
  setTimeout(() => { 
    console.log("resolve 1"); 
    resolve(1); 
  }, 2000); 
}); 
 
const b = new Promise((resolve, reject) => { 
  setTimeout(() => { 
    console.log("reject 2"); 
    reject(2); 
  }, 1000); 
}); 
 
const c = new Promise((resolve, reject) => { 
  console.log("resolve"); 
  resolve(3); 
}); 
 
Promise.all([a, b, c]).then( 
  r => { 
    console.log(r); 
  }, 
  () => { 
    console.log("err"); 
  }, 
);

выведет

resolve 3
reject 2
err
resolve 1

т.е. все завершилось без ожидания завершения промиса a (resolve 1 выводится после err). Вопрос в том, как дождаться, пока все промисы будут выполнены и/или отклонены?

Answer 1

Очень просто: замаскировать ошибку через вызов catch.

const a = new Promise((resolve, reject) => { 
  setTimeout(() => { 
    console.log("resolve 1"); 
    resolve(1); 
  }, 2000); 
}); 
 
const b = new Promise((resolve, reject) => { 
  setTimeout(() => { 
    console.log("reject 2"); 
    reject(2); 
  }, 1000); 
}); 
 
const c = new Promise((resolve, reject) => { 
  console.log("resolve 3"); 
  resolve(3); 
}); 
 
Promise.all([a, b, c].map(p => p.catch(x => console.error(x)))).then( 
  r => { 
    console.log(r); 
  } 
);

Результат: [1, undefined, 3].

Answer 2

Вот как-то так получилось (не знаю на сколько это говнокод), я сделал функцию, которая модифицирует промисы, добавляя им свойство isPending, в методе waitForAll я каждому промису добавляю обработчики в then, в которых при успешном/неуспешном выполнени промиса пробегаю входной массив и проверяю, у всех ли промисов в масиве isPending == false, если так, значит все промисы уже завершились.

const a = MakePromise(new Promise((resolve, reject) => { 
  setTimeout(() => { 
    console.log("resolve 1"); 
    resolve(1); 
  }, 2000); 
})); 
 
const b = MakePromise(new Promise((resolve, reject) => { 
  setTimeout(() => { 
    console.log("reject 2"); 
    reject(2); 
  }, 1000); 
})); 
 
const c = MakePromise(new Promise((resolve, reject) => { 
  console.log("resolve"); 
  resolve(3); 
})); 
 
waitForAll([a, b, c]); 
 
function waitForAll(arr) { 
  arr.forEach((p) => { 
    p.then( 
      (r) => { 
        if (allDone(arr)) console.log('all done'); 
      }, 
      (e) => { 
        if (allDone(arr)) console.log('all done'); 
      }); 
  }); 
}; 
 
function allDone(arr) { 
  for (let i = 0; i < arr.length; i++) { 
    if (arr[i].isPending()) return false; 
  } 
  return true; 
}; 
 
function MakePromise(promise) { 
  var isPending = true; 
 
  var result = promise.then( 
    (v) => { 
      isPending = false; 
      return v; 
    }, 
    (e) => { 
      isPending = false; 
      throw e; 
    } 
  ); 
 
  result.isPending = () => { 
    return isPending; 
  }; 
  return result; 
}

Я понял что предыдущий вариант явно не очень, сделал через каунтер, тоесть функция waitForAll получает массив промисов, сразу записываем в переменную длину массива (количество промисов) и в обработчиках then уменьшаем ету переменную ну и сравниваем ее с 0, тоесть если количество 0, значит все промисы выполнены :

const a = new Promise((resolve, reject) => { 
  setTimeout(() => { 
    console.log("resolve 1"); 
    resolve(1); 
  }, 2000); 
}); 
 
const b = new Promise((resolve, reject) => { 
  setTimeout(() => { 
    console.log("reject 2"); 
    reject(2); 
  }, 1000); 
}); 
 
const c = new Promise((resolve, reject) => { 
  console.log("resolve"); 
  resolve(3); 
}); 
 
waitForAll([a, b, c]); 
 
function waitForAll(arr) { 
  var count = arr.length; 
  arr.forEach((p) => { 
    p.then( 
      (r) => { 
        count--; 
        if (count == 0) console.log('all done'); 
      }, 
      (e) => { 
        count--; 
        if (count == 0) console.log('all done'); 
      }); 
  }); 
};

Answer 3

const a = new Promise((resolve, reject) => { 
  setTimeout(() => { 
    console.log("resolve 1"); 
    resolve(1); 
  }, 2000); 
}); 
 
const b = new Promise((resolve, reject) => { 
  setTimeout(() => { 
    console.log("reject 2"); 
    reject(2); 
  }, 1000); 
}); 
 
const c = new Promise((resolve, reject) => { 
  console.log("resolve"); 
  resolve(3); 
}); 
 
Promise.all([a, b, c].map(p => p.then( 
  x => ({ resolved: x }), 
  e => ({ rejected: e }) 
))).then(results => { 
  console.log('=== ALL ==='); 
   
  for (var res of results) { 
    if ('resolved' in res) { // Not res.resolved because `resolve(0)` 
      console.log(res.resolved); 
    } else { 
      console.log('error', res.rejected); 
    } 
  } 
});

READ ALSO
Как отменить событие scroll при достижении условия?

Как отменить событие scroll при достижении условия?

Допустим есть событие scroll $(window)scroll(function() { следящее за прокруткой

342
Ограничения на название ключей в JSON

Ограничения на название ключей в JSON

Разбираю файл JSON с помощью JSONparse(); Выясняется такая вещь, что ключ с дефисом не работает

295
node js не работает на хостинге

node js не работает на хостинге

Возникла проблема с запуском node js серверной части на удаленном хостинге

358
Как написать юнит-тест на замыкание?

Как написать юнит-тест на замыкание?

не пойму, как такую функцию вызвать в юнит-тесте?

234