Как мне использовать обещания для вызовов db + http в узле js
Мне нужно реализовать систему, которая
- Получить данные из родительской коллекции.
- Проверьте, найден ли определенный ключ в Redis
- Если нет, тогда выполните http-вызов и получите данные json, затем установите кеш
- если да, то получить данные из кеша
- сохранить данные в дочернюю коллекцию для родительского идентификатора.
У меня есть рабочее решение с помощью обратного вызова что-то подобное.
MongoClient.connect(dsn).then(function(db) {
parentcollection.findOne({"_id" : new ObjectId(pid)}, function(err, data) {
var redis = require("redis"),
client = redis.createClient();
client.on("error", function (err) {
console.log("Error " + err);
});
// If not set
client.get(cacheKey, function(err, data) {
// data is null if the key doesn't exist
if(err || data === null) {
var options = {
host: HOST,
port: 80,
path: URI
};
var req = http.get(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
body += chunk;
//console.log('CHUNK: ' + chunk);
});
res.on('end', function () {
data = JSON.parse(body);
// Get childdata After process of data
childcollection.save(childdata, {w:1}, function(cerr, inserted) {
db.close();
});
});
});
} else {
// Get childdata from cache data
childcollection.save(childdata, {w:1}, function(cerr, inserted) {
db.close();
});
}
});
});
Я хочу использовать обещание (нативное, а не внешнее, например, bluebird / request) вместо обратных вызовов. Я проверяю руководства и думаю, нужно ли мне это реализовать
var promise1 = new Promise((resolve, reject) => {
MongoClient.connect(dsn).then(function(db) {
parentcollection.findOne({"_id" : new ObjectId(pid)}, function(err, data) {
});
}}.then(function(data){
var promise2 = new Promise((resolve, reject) => {
var redis = require("redis"),
client = redis.createClient();
client.on("error", function (err) {
console.log("Error " + err);
});
// If not set
client.get(cacheKey, function(err, data) {
// data is null if the key doesn't exist
if(err || data === null) {
var options = {
host: HOST,
port: 80,
path: URI
};
var promise3 = new Promise((resolve, reject) => {
var req = http.get(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
body += chunk;
//console.log('CHUNK: ' + chunk);
});
res.on('end', function () {
data = JSON.parse(body);
// Get childdata After process of data
});
})
}).then(function(data){
childcollection.save(childdata, {w:1}, function(cerr, inserted) {
db.close();
});
});
} else {
// Get childdata from cache data
childcollection.save(childdata, {w:1}, function(cerr, inserted) {
db.close();
});
}
});
}}.then(function(data){
});
});
Которые выглядят так же грязно, как ад обратного вызова или какой-то лучший подход, который не использовал обещания, как выше?
1 ответ
Одна из проблем заключается в том, что вы никогда не вызываете функции разрешения, предоставленные обратным вызовам конструктора обещаний. Без вызова их обещания никогда не решаются.
Я бы предложил создать эти новые обещания в отдельных, многократно используемых функциях. С другой стороны, некоторые методы MongoDb возвращают обещания уже тогда, когда вы не предоставляете аргумент обратного вызова.
Вы можете сделать это, как показано ниже.
// Two promisifying functions:
function promiseClientData(client, key) {
return new Promise(function (resolve, reject) {
return client.get(key, function (err, data) {
return err ? reject(err) : resolve(data); // fulfull the promise
});
});
}
function promiseHttpData(options) {
return new Promise(function (resolve, reject) {
return http.get(options, function(res) {
var body = ''; // You need to initialise this...
res.setEncoding('utf8');
res.on('data', function (chunk) {
body += chunk;
//console.log('CHUNK: ' + chunk);
});
res.on('end', function () {
data = JSON.parse(body);
resolve(data); // fulfull the promise
});
);
});
}
// Declare the db variable outside of the promise chain to avoid
// having to pass it through
var db;
// The actual promise chain:
MongoClient.connect(dsn).then(function (dbArg) {
db = dbArg;
return parentcollection.findOne({"_id" : new ObjectId(pid)}); // returns a promise
}).then(function (data) {
var redis = require("redis"),
client = redis.createClient();
client.on("error", function (err) {
console.log("Error " + err);
});
// Get somehow cacheKey...
// ...
return promiseClientData(client, cacheKey);
}).then(function (data) {
// If not set: data is null if the key doesn't exist
// Throwing an error will trigger the next `catch` callback
if(data === null) throw "key does not exist";
return data;
}).catch(function (err) {
var options = {
host: HOST,
port: 80,
path: URI
};
return promiseHttpData(options);
}).then(function (data) {
// Get childdata by processing data (in either case)
// ....
// ....
return childcollection.save(childdata, {w:1}); // returns a promise
}).then(function () {
db.close();
});
Я предполагаю, что обещания, возвращенные MongoDb, полностью соответствуют. В сомнении, вы можете превратить их в обещания JavaScript, позвонив Promise.resolve()
на них, например, так:
return Promise.resolve(parentcollection.findOne({"_id" : new ObjectId(pid)}));
или же:
return Promise.resolve(childcollection.save(childdata, {w:1}));