Как Q.all работает в NodeJS?

У меня есть следующий код:

var a = [1,2,3,4,5];
var promises = [];
a.forEach(function(item,index){
    var deferred = Q.defer();
    doSomething().then(function(){
        deferred.resolve(true);
        promises.push(deferred);
    });
});

Q.all(promises).then(function(data){
    console.log("something!!");
});

Откуда Q.all знает, что в массиве promises есть все обещания, необходимые для цикла forEach? Иногда мой Q.all запускается перед forEach. Пожалуйста, скажите мне, где я ошибаюсь.

doSomething() — это асинхронная функция, которая возвращает обещание.


person skjindal93    schedule 10.06.2015    source источник
comment
Можете ли вы привести какой-нибудь практический пример? Вы игнорируете item и index. Даже если вы получите ответы, я не уверен, что вы можете напрямую использовать их для решения своей реальной проблемы.   -  person thefourtheye    schedule 10.06.2015


Ответы (2)


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

Но на самом деле вы помещаете элементы в массив после вызова Q.all. Ваш способ использования промисов немного неудобен: не нужно использовать отложенный в промисе!

Кроме того, вы не хотите нажимать deffered, а обещание, которое оно держит, после его отклонения или разрешения. См. ниже дополнительную информацию о том, как «обещать» асинхронную функцию.

Отложенные используются для определения промисов из простого асинхронного кода на основе обратного вызова. Поскольку doSomething() возвращает обещание (вы используете .then()), вы можете просто сделать:

var a = [1,2,3,4,5];
var promises = [];
a.forEach(function(item,index){
    var promise = doSomething().then(function(data){
        return Q(true);
    });
    promises.push(promise);
});

Q.all(promises).then(function(data){
    console.log("something!!");
});

Затем промисы будут напрямую заполнены промисами без каких-либо задержек.

РЕДАКТИРОВАТЬ: поскольку вы спрашиваете о том, что doSomething не поддерживает обещание, вот что вы можете сделать:

Допустим, doSomething принимает в качестве параметра обратный вызов для выполнения после некоторой асинхронной задачи.

Затем вы можете обернуть doSomething таким образом:

function doSomethingPromise(){
    var defered = Q.defer();
    doSomething(function(err,data){
       if(err){
           defered.reject(err);
       }
       else{
           defered.resolve(data);
       }
    });
    return defered.promise;
}

а затем используйте doSomethingPromise(), как указано выше, вместо doSomething, так как этот возвращает обещание.

person Tiesselune    schedule 10.06.2015
comment
Спасибо! А что, если я не верну обещание в doSomething? то есть doSomething - это просто асинхронная функция, и я хочу запустить Q.all после завершения forEach. - person skjindal93; 10.06.2015
comment
Я обновил свой ответ, чтобы объяснить, как обещать асинхронную функцию без обещаний. - person Tiesselune; 10.06.2015
comment
Я думаю, что @skjindal93 слишком упростил свой код; значение элемента даже не используется. Я не думаю, что это та проблема, на которой мы должны сосредоточиться. - person Tiesselune; 10.06.2015
comment
Большое спасибо Тисселун. - person skjindal93; 10.06.2015
comment
Привет, это хорошая отправная точка. Мне было интересно, как ловить ошибки в doSomething() внутри цикла forEach. Это будет так?: doSomething() .then(d =› {return Q(true);}) .catch(e =› {return Q(e);}); - person Vladimir Venegas; 18.11.2016
comment
@Tiesselune может ли этот Q.all быть вложенным несколько раз? - person Sundeep Pidugu; 02.03.2021
comment
Q.All возвращает обещание, которое можно добавить в массив, для которого вы вызываете Q.all, и так далее, так что да, это возможно. Я просто добавлю, что теперь, когда существуют обещания ES6, Q немного менее актуален, поскольку вы можете делать все, что он делает, без использования библиотеки. Но если в вашем проекте используется Q, что ж, придерживайтесь его, если не хотите конвертировать все свои промисы в стандартные - person Tiesselune; 02.03.2021

Проблема в том, что при выполнении Q.all массив promises все еще пуст. Отложенные объекты помещаются в promises асинхронно, поэтому Q.all() будет выполняться до того, как обещание от doSomething() будет разрешено.

person Mike    schedule 10.06.2015