#javascript #node.js #postgresql #sequelize.js
#javascript #node.js #postgresql #sequelize.js
Вопрос:
У меня есть пользовательские и файловые модели в Sequlize. У пользователя может быть несколько файлов. У меня есть ассоциация db.User.hasMany(db.File, {as: ‘Files’, ForeignKey: ‘userId’, ограничения: false});
Я хочу инициализировать пользовательский объект с несколькими файлами и сохранить его в базе данных.
Я написал следующий код:
var files = [];
var file1 = models.File.build();
file1.name = "JPEG";
file1.save().then(function () {
});
files.push(file1);
var file2 = models.File.build();
file2.name = "PNG";
file2.save().then(function () {
});
files.push(file2);
var newUser = models.User.build();
newUser.email = email;
newUser.save().then(function (usr) {
files.forEach(function (item) {
newUser.addFile(item);
});
});
Но я обнаружил ошибку, иногда несколько файлов не были связаны с пользователем.
Я нашел (в журналах nodejs) команды (update) для установки внешних ключей для этих файлов. Но команды не были выполнены. Внешние ключи (userId) нескольких файлов были пустыми.
Я думаю, что проблема в асинхронных запросах. Как организовать структуру кода, чтобы избежать этой ошибки?
Комментарии:
1. почему вы не создаете файлы после пользователя и не добавляете идентификатор пользователя в определение file?
Ответ №1:
Проблема именно в том, что вы думаете, в асинхронном коде.
Вам нужно переместить функции внутри обратных вызовов, иначе код будет запущен до создания файла.
JavaScript не ждет, прежде чем перейти к следующей строке, поэтому он запустит следующую строку, независимо от того, сделано это или нет. Нет смысла ждать перед перемещением.
Таким образом, вы в основном добавляете что-то, что еще не существует, потому что оно не ожидает сохранения файла, прежде чем двигаться дальше.
Это сработало бы, просто переместив код внутри обратных then
вызовов:
var files = [];
var file1 = models.File.build();
file1.name = "JPEG";
file1.save().then(function () {
files.push(file1);
var file2 = models.File.build();
file2.name = "PNG";
file2.save().then(function () {
files.push(file2);
var newUser = models.User.build();
newUser.email = email;
newUser.save().then(function (usr) {
files.forEach(function (item) {
newUser.addFile(item);
});
});
});
});
Но это грязно. Вместо этого вы можете связать обещания следующим образом:
var files = [];
var file1 = models.File.build();
file1.name = "JPEG";
file1.save()
.then(function(file1) {
files.push(file1);
var file2 = models.File.build();
file2.name = "PNG";
return file2.save();
})
.then(function(file2) {
files.push(file2);
var newUser = models.User.build();
newUser.email = email;
return newUser.save();
})
.then(function(newUser) {
files.forEach(function(item) {
newUser.addFile(item);
});
});
Теперь это немного чище, но все еще немного запутанно и немного сбивает с толку. Таким образом, вы можете использовать функции генератора вместо этого следующим образом:
var co = require('co');
co(function*() {
var files = [];
var file1 = models.File.build();
file1.name = "JPEG";
yield file1.save();
files.push(file1);
var file2 = models.File.build();
file2.name = "PNG";
yield file2.save();
files.push(file2);
var newUser = models.User.build();
newUser.email = email;
newUser.save();
files.forEach(function(item) {
newUser.addFile(item);
});
});
Теперь это намного лучше.
Посмотрите внимательно, и вы увидите, что происходит. co
принимает функцию генератора, которая в основном является обычной функцией со звездочками *
. Это специальная функция, которая добавляет yield
поддержку выражений.
yield
выражения в основном ожидают then()
вызова обратного вызова, прежде чем двигаться дальше, и если у then
обратного вызова есть аргумент, он также вернет его.
Итак, вы можете сделать что-то вроде:
var gif = yield models.File.create({
name: 'gif'
});
вместо:
models.File.create({
name: 'gif'
}).then(function(gif) {
});
Вы должны использовать небольшой модуль узла, вызываемый co
для этого, хотя просто npm install --save co