2015-01-23 2 views
0

у меня есть модель что-то вроде этого:sailsJS .exec() в forloop не работает

\** 
* user.petIds = ["1","2","3","4"]; 
*\ 
getPets: function(users){ 
    var response = []; 
    users.forEach(function(user){ 
     Pet.find(users[i].petIds).exec(function(err, pets){ 
     if(err) return null; 
     console.log("index: "+i); 
     response.push({ name: users[i].name, pets: pets}); 
     }); 
    }); 
    return response; 
    } 

Я хочу, чтобы каждый из домашних животных массива животных идентификаторами для массива пользователей. Тем не менее, exec выполняет только после обратного вызова, который в этом случае вызывает возврат преждевременно. Есть что-нибудь в Sails, которые решают эту проблему? В противном случае невозможно использовать цикл for для запроса. Я все еще очень новичок в том, как работает JS. Любая помощь приветствуется. Спасибо (:

ответ

2

Это не проблема с Sails, это общая проблема с узлом, с которым вы столкнетесь, когда узнаете больше об асинхронном программировании. Если вы ищете StackOverflow для «асинхронного цикла node.js», вы найдете dozens of questions and answers. Проблема заключается в том, что вы вызываете асинхронную функцию (.exec()) внутри синхронного цикла for, поэтому все итерации цикла (и return response) происходят до того, как вызываются какие-либо действительные обратные вызовы (функции function(err, pets)).

Самый простой способ решить эту проблему - использовать библиотеку, такую ​​как async, для асинхронного запуска цикла. Именно по этой причине async включен и глобализованы парусами по умолчанию, так что вы можете сделать:

// Note the new second argument to "getPets", since this is 
// an asynchronous function it requires a callback 
getPets: function(users, getPetsCb){ 

    // Asynchronous "map" call to transform users 
    async.map(users, function iterator (user, mapCb){ 

     // Get the user's pets 
     Pet.find(user.petIds).exec(function(err, pets) { 

     // In case of error pass it to the callback, which 
     // will abort the loop 
     if(err) return mapCb(err); 

     // Use "null" as the first arg to indicate no error has 
     // occurred, and pass the transformed user as the second 
     // to add it to the array 
     return mapCb(null, { name: user.name, pets: pets}); 
     }); 
    }, 
    // Pass the outer callback as the 3rd argument to async.map 
    // to pass the final error state and array of transformed 
    // users to whoever called "getPets" 
    getPetsCb); 
} 

Читать full docs for async.map here, а также все другие забавные async методы.

Также обратите внимание, что в вашем конкретном случае вы можете использовать Sails associations для моделирования отношений между пользователем и домашним животным и получить всю эту информацию одним запросом: User.find(...).populate('pets').exec(...)!

+0

Итак, я предполагаю, что getPets возвращает функцию (err, response)? – holyxiaoxin

+0

'getPets' ничего не возвращает. Это то, что вам нужно понять о асинхронном программировании. Чтобы обработать результаты того, что происходит в 'getPets', вы должны передать функцию обратного вызова в качестве аргументов' getPetsCb'. Эта функция может быть любой, но должна иметь подпись '(err, result)', которая является типичной для обратных вызовов Node. – sgress454

+0

Я получил это. Большое спасибо за помощь. Это заставило меня хорошо понять асинхронное программирование. (: – holyxiaoxin

2

Если вы используете Sails .10 или выше, вы можете использовать ассоциации моделей, чтобы справиться с этим, если подойти к проблеме немного по-другому. Фактически они детализируют очень похожий случай использования в documentation.

Если вы настраиваете ваши модели таким образом:

MYAPP/API/модели/pet.js

module.exports = { 
    attributes: { 
     name:'STRING', 
     // include whatever other attributes 
     owner:{ 
      model:'user' 
     } 
    } 
} 

MYAPP/API/модели/user.js

module.exports = { 
    attributes: { 
     name:'STRING', 
     // other attributes you may want included here 
     pets:{ 
      collection: 'pet', 
      via: 'owner' 
     } 
    } 
} 

Тогда в вашем контроллере вы можете использовать следующее, чтобы найти всех домашних животных для данного пользователя:

getPets: function(req, res) { 
    User.find() 
     .where({id: users}) 
     .populate('pets') 
     .exec(function(err, users) { 
     if (err) throw err; // do error handling here 

     return res.json({ 
      users: users 
     }); 
    }); 
} 

Таким образом, JSON, который отправляется обратно к вам, имеет каждого пользователя и всех домашних животных этого пользователя, как показано здесь.

{ 
    users: 
     [{ 
      pets: 
       [ { name: 'Spot', 
        id: 2, 
        createdAt: Tue Feb 11 2014 17:58:04 GMT-0600 (CST), 
        updatedAt: Tue Feb 11 2014 17:58:04 GMT-0600 (CST), 
        owner: 1 }, 
       { name: 'Sparky', 
        id: 4, 
        createdAt: Tue Feb 11 2014 18:02:58 GMT-0600 (CST), 
        updatedAt: Tue Feb 11 2014 18:02:58 GMT-0600 (CST), 
        owner: 1 } ], 
       name: 'Mike', 
       createdAt: Tue Feb 11 2014 17:49:04 GMT-0600 (CST), 
       updatedAt: Tue Feb 11 2014 17:49:04 GMT-0600 (CST), 
       id: 1 
     }] 
} 

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

+0

Спасибо за предложение ассоциаций.У меня есть ассоциации, и я хотел бы изучить другие методы. Благодаря! (: – holyxiaoxin

+0

Я хотел бы добавить, что объединение таблиц будет стоить дороже, чем выбор. Поэтому я также пытаюсь найти способы оптимизации запроса. Надеюсь, я на правильном пути. – holyxiaoxin

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