Проблеми с кода
Добре, тук има много проблеми, така че първо.
connection.query('...', function (err, rows) {
connection.release();
if (!err) {
return rows;
} else {
return false;
}
});
Това няма да работи, защото връщате данни на обаждащия се, което е заявката към базата данни, която извиква вашето обратно извикване с err
и rows
и не се интересува от върнатата стойност на вашето обратно извикване.
Това, което трябва да направите, е да извикате някаква друга функция или метод, когато имате редовете или когато нямате.
Обаждате се:
var rows = loginM.findUser(req.body, res);
и очаквате да получите редовете там, но няма да го направите. Това, което ще получите, е undefined
и ще го получите по-бързо, отколкото дори е стартирана заявката за база данни. Работи така:
me.findUser = function(params, res) {
// (1) you save the username in a variable
var username = params.username;
// (2) you pass a function to getConnection method
pool.getConnection(function (err, connection) {
console.log("Connection ");
if (err) {
console.log("ERROR 1 ");
res.send({"code": 100, "status": "Error in connection database"});
return;
}
connection.query('select Id, Name, Password from Users ' +
'where Users.Name = ?', [username], function (err, rows) {
connection.release();
if (!err) {
return rows;
} else {
return false;
}
});
//connection.on('error', function (err) {
// res.send({"code": 100, "status": "Error in connection database"});
// return;
//});
});
// (3) you end a function and implicitly return undefined
}
pool.getConnection
методът се връща веднага след като подадете функция, преди дори да се осъществи връзката с базата данни. След това, след известно време, тази функция, която сте предали на този метод, може да бъде извикана, но ще бъде дълго след като вече сте върнали undefined
към кода, който иска стойност в:
var rows = loginM.findUser(req.body, res);
Вместо да връщате стойности от обратно извикване, трябва да извикате някои други функции или методи от тях (като някои обратни извиквания, които трябва да извикате, или метод за разрешаване на обещание).
Връщането на стойност е синхронна концепция и няма да работи за асинхронен код.
Как трябва да се използват обещанията
Сега, ако функцията ви върна обещание :
me.findUser = function(params, res) {
var username = params.username;
return new Promise(function (res, rej) {
pool.getConnection(function (err, connection) {
console.log("Connection ");
if (err) {
rej('db error');
} else {
connection.query('...', [username], function (err, rows) {
connection.release();
if (!err) {
res(rows);
} else {
rej('other error');
}
});
});
});
}
тогава ще можете да го използвате в друга част от кода си по начин като този:
app.post('/login/', function(req, res, next) {
var promise = new Promise(function (resolve, reject) {
// rows is a promise now:
var rows = loginM.findUser(req.body, res);
rows.then(function (rowsValue) {
console.log("Success");
resolve(rowsValue);
}).catch(function (err) {
console.log("Failed");
reject(err);
});
});
// ...
Обяснение
В обобщение, ако изпълнявате асинхронна операция - като заявка за база данни - тогава не можете да имате стойността веднага така:
var value = query();
защото сървърът ще трябва да блокира изчакването на базата данни, преди да може да изпълни заданието - и това се случва на всеки език със синхронен, блокиращ I/O (затова трябва да имате нишки на тези езици, за да могат да бъдат други неща извършено, докато тази нишка е блокирана).
В Node можете да използвате функция за обратно извикване, която предавате на асинхронната функция, за да бъде извикана, когато има данни:
query(function (error, data) {
if (error) {
// we have error
} else {
// we have data
}
});
otherCode();
Или можете да получите обещание:
var promise = query();
promise.then(function (data) {
// we have data
}).catch(function (error) {
// we have error
});
otherCode();
Но и в двата случая otherCode()
ще се стартира веднага след регистриране на вашите манипулатори за обратно извикване или обещание, преди заявката да има някакви данни - това не е необходимо да се прави блокиране.
Резюме
Цялата идея е, че в асинхронна, неблокираща, еднонишкова среда като Node.JS никога не правите повече от едно нещо наведнъж - но можете да чакате за много неща. Но вие не просто чакате нещо и не правите нищо, докато чакате, вие планирате други неща, чакате още неща и в крайна сметка ви се обаждат отново, когато е готово.
Всъщност написах кратка история на Medium, за да илюстрирам тази концепция:Нечерен I/O на планетата Asynchronia256/16 – Кратка история, основана свободно на несигурни факти .