Вместо того, чтобы бросать код на вас, есть два понятия, которые являются ключом к пониманию того, как JS обрабатывает обратные вызовы и асинхронность. (это даже слово?)
Есть три вещи, о которых вам нужно знать; Очередь; цикл события и стек
. В широких упрощенных терминах цикл событий подобен диспетчеру проекта, он постоянно прослушивает любые функции, которые хотят запускать и взаимодействовать между очереди и стека.
while (queue.waitForMessage()) {
queue.processNextMessage();
}
Как только он получает сообщение для запуска чего-то, он добавляет его в очередь. Очередь - это список вещей, которые ждут выполнения (например, ваш запрос AJAX). Представьте себе это так:
1. call foo.com/api/bar using foobarFunc
2. Go perform an infinite loop
... and so on
Когда одно из этих сообщений будет исполнено, оно выталкивает сообщение из очереди и создает стек, стек - это все, что нужно выполнить JS для выполнения инструкции в сообщение. Таким образом, в нашем примере ему говорят позвонить foobarFunc
function foobarFunc (var) {
console.log(anotherFunction(var));
}
. Так что все, что foobarFunc должно выполнить (в нашем случае anotherFunction
), будет вставлено в стек. исполняемый, а затем забытый - цикл события затем переместится на следующую вещь в очереди (или прослушивает сообщения)
. Главное здесь - порядок выполнения. Это
Когда вы совершаете вызов с использованием AJAX для внешней стороны или выполняете любой асинхронный код (например, setTimeout), Javascript зависит от ответ, прежде чем он сможет продолжить.
Большой вопрос, когда он получит ответ? Ответ в том, что мы не знаем, поэтому цикл событий ждет, когда это сообщение скажет: «Эй, забери меня». Если JS просто ждал этого сообщения синхронно, ваше приложение замерзнет, и оно сосать. Таким образом, JS продолжает выполнение следующего элемента в очереди, ожидая, пока сообщение не будет добавлено обратно в очередь.
Вот почему с асинхронной функциональностью мы используем вещи, называемые обратными вызовами. Это похоже на обещание буквально. Как и в I , обещание что-то вернуть в какой-то момент jQuery использует специальные обратные вызовы, называемые deffered.done
deffered.fail
и deffered.always
(среди других). Вы можете увидеть их все здесь
Итак, вам нужно передать функцию, которая в какой-то момент будет выполнена с переданными ей данными.
Поскольку обратный вызов не выполняется немедленно, но в более позднее время важно передать ссылку на функцию, которую она не выполнила. поэтому
function foo(bla) {
console.log(bla)
}
, поэтому большую часть времени (но не всегда) вы пройдете foo
не foo()
. Надеюсь, это будет иметь смысл. Когда вы сталкиваетесь с такими вещами, которые кажутся запутанными, я настоятельно рекомендую полностью прочитать документацию, чтобы хотя бы понять ее. Это сделает вас намного лучшим разработчиком.
Вы должны иметь возможность использовать функцию заполнения модели для этого: http://mongoosejs.com/docs/api.html#model_Model.populate В обработчике сохранения для книги вместо :
book._creator = user;
вы бы сделали что-то вроде:
Book.populate(book, {path:"_creator"}, function(err, book) { ... });
Возможно, слишком поздно ответ, чтобы помочь вам, но я застрял на этом недавно, и это может быть полезно для других.
Я подумал, что добавлю к этому, чтобы прояснить ситуацию для полных нубов, подобных мне.
Что сильно запутывает, если вы не внимательны, так это три разных метода заполнения. Они - методы разных объектов (Model vs. Document), принимают разные входы и дают разные выходы (Document vs. Promise).
Здесь они предназначены для тех, кто сбиты с толку:
Этот файл работает с документами и возвращает документ. В исходном примере это будет выглядеть так:
book.save(function(err, book) {
book.populate('_creator', function(err, book) {
// Do something
})
});
Поскольку он работает с документами и возвращает документ, вы можете связать их следующим образом:
book.save(function(err, book) {
book
.populate('_creator')
.populate('/* Some other ObjectID field */', function(err, book) {
// Do something
})
});
Но не будьте глупыми, как я, и попытайтесь сделать это:
book.save(function(err, book) {
book
.populate('_creator')
.populate('/* Some other ObjectID field */')
.then(function(book) {
// Do something
})
});
Помните: Document.prototype.populate () возвращает документ, поэтому это вздор. Если вам нужно обещание, вам нужно ...
Это работает над документами, но возвращает обещание, которое разрешает документ. Другими словами, вы можете использовать его следующим образом:
book.save(function(err, book) {
book
.populate('_creator')
.populate('/* Some other ObjectID field */')
.execPopulate()
.then(function(book) {
// Do something
})
});
Это лучше. Наконец, есть ...
Это работает на моделях и возвращается Обещание. Поэтому он используется несколько иначе:
book.save(function(err, book) {
Book // Book not book
.populate(book, { path: '_creator'})
.then(function(book) {
// Do something
})
});
Надеюсь, что это помогло другим новичкам.
В случае, если кто-то все еще ищет это.
Mongoose 3.6 представила множество интересных функций для заполнения:
book.populate('_creator', function(err) {
console.log(book._creator);
});
или:
Book.populate(book, '_creator', function(err) {
console.log(book._creator);
});
подробнее см .: https://github.com/LearnBoost/mongoose/wiki/3.6-Release-Notes#population
Но таким образом вы все равно будете запрашивать для пользователя снова.
Небольшой трюк для выполнения этого без дополнительных запросов будет:
book = book.toObject();
book._creator = user;
Решение для меня заключалось в использовании execPopulate
, например
const t = new MyModel(value)
return t.save().then(t => t.populate('my-path').execPopulate())
К сожалению, это долговременная проблема с мангустом, который, я считаю, еще не решен:
https://github.com/LearnBoost/mongoose/issues/570
Что вы можете сделать, это написать собственный пользовательский getter / setter (и установить real _customer
в отдельном свойстве) для этого. Например:
var get_creator = function(val) {
if (this.hasOwnProperty( "__creator" )) {
return this.__creator;
}
return val;
};
var set_creator = function(val) {
this.__creator = val;
return val;
};
var bookSchema = new mongoose.Schema({
_creator: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
get: get_creator,
set: set_creator
},
description: String,
});
ПРИМЕЧАНИЕ. Я не тестировал его, и он мог бы работать странно с .populate
и при установке чистого идентификатора.
Использовать документ # populate
book.populate('creator').execPopulate();
// summary
doc.populate(options); // not executed
doc.populate(options).execPopulate() // executed, returns promise
Возможная реализация
var populatedDoc = doc.populate(options).execPopulate();
var populatedDoc.then(doc => {
...
});
здесь .
Вероятно, шт. например
Book.createAsync(bookToSave).then((savedBook) => savedBook.populateAsync("creator"));
Было бы самым приятным и наименее проблематичным способом сделать эту работу (используя обещания Bluebird).
Просто чтобы уточнить и привести еще один пример, поскольку это помогло мне. Это может помочь тем, кто хочет получить частично заполненные объекты после сохранения. Метод также немного отличается. Прошло более часа или двух, ища правильный способ сделать это.
post.save(function(err) {
if (err) {
return res.json(500, {
error: 'Cannot save the post'
});
}
post.populate('group', 'name').populate({
path: 'wallUser',
select: 'name picture'
}, function(err, doc) {
res.json(doc);
});
});
Mongoose 5.2.7
Это работает для меня (просто много головной боли!) [/ g1]
exports.create = (req, res, next) => {
const author = req.userData;
const postInfo = new Post({
author,
content: req.body.content,
isDraft: req.body.isDraft,
status: req.body.status,
title: req.body.title
});
postInfo.populate('author', '_id email role display_name').execPopulate();
postInfo.save()
.then(post => {
res.status(200).json(post);
}).catch(error => {
res.status(500).json(error);
});
};