Как отправить несколько запросов из одной конечной точки с помощью Express?
Я пытаюсь сделать запрос к моей базе данных несколько раз и создать объект, который хранит каждый ответ из моей базы данных в поле. Вот мой код:
router.post('/search', (req, res) => {
var collection = db.get().collection('styles')
var data = [];
collection.distinct('make.name', (err, docs) => {
data.push({'make': docs });
});
collection.distinct('model', (function (err, docs) {
data.push({'model': docs });
}))
res.send(data);
});
Поскольку NodeJS/Express является асинхронным, это не работает так, как мне бы хотелось. Как я могу восстановить эту конечную точку, чтобы сделать несколько вызовов базы данных (из одной коллекции) и вернуть объект, содержащий ее?
2 ответа
Есть несколько способов сделать это:
Вложенные обратные вызовы
Без обещаний вы можете вкладывать обратные вызовы:
router.post('/search', (req, res) => {
var collection = db.get().collection('styles')
var data = [];
collection.distinct('make.name', (err, docs) => {
if (err) {
// ALWAYS HANDLE ERRORS!
}
data.push({'make': docs });
collection.distinct('model', (function (err, docs) {
if (err) {
// ALWAYS HANDLE ERRORS!
}
data.push({'model': docs });
res.send(data);
}))
});
});
Это был бы самый простой способ, но учтите, что он неэффективен, если эти два запроса могут выполняться параллельно.
async
модуль
Вы можете использовать async
модуль:
router.post('/search', (req, res) => {
var collection = db.get().collection('styles')
var data = [];
async.parallel({
make: cb => collection.distinct('make.name', cb),
model: cb => collection.distinct('model', cb),
}, (err, responses) => {
if (err) {
// ALWAYS HANDLE ERRORS!
}
data.push({'make': responses.make });
data.push({'model': responses.model });
res.send(data);
});
});
Смотрите: https://caolan.github.io/async/docs.html
Но это все еще не самый удобный метод.
ES2017 async
/await
Наиболее гибкий способ сделать это, если у вас есть 30 звонков, это:
- Используйте функции, которые возвращают обещания вместо функций, которые принимают обратные вызовы
- Используйте async / await, если можете, или хотя бы сопрограммы на основе генератора
- Ожидание обещаний (или выдача обещаний), когда логика должна выполняться последовательно
- использование
Promise.all()
для всего, что может быть сделано параллельно
С помощью async / await ваш код может выглядеть так:
// in sequence:
var make = await collection.distinct('make.name');
var model = await collection.distinct('model');
// use 'make' and 'model'
Или же:
// in parallel:
var array = await Promise.all([
collection.distinct('make.name'),
collection.distinct('model'),
]);
// use array[0] and array[1]
Большое преимущество async
/await
это обработка ошибок:
try {
var x = await asyncFunc1();
var array = await Promise.all([asyncFunc2(x), asyncFunc3(x)]);
var y = asyncFunc4(array);
console.log(await asyncFunc5(y));
} catch (err) {
// handle any error here
}
Вы можете использовать его только внутри функции, созданной с помощью async
ключевое слово. Для получения дополнительной информации см.:
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
Для поддержки в браузерах, смотрите:
Для поддержки в узле см.:
В местах, где у вас нет собственной поддержки async
а также await
Вы можете использовать Babel:
или с немного другим синтаксисом подход на основе генератора, как в co
или сопрограммы Bluebird:
Смотрите эти ответы для получения дополнительной информации:
- попробуйте / поймать блоки с асинхронным / ожиданием
- node.js ~ построение цепочки последовательности Promise решает
- Как запустить функции генератора параллельно?
- node.js ~ построение цепочки последовательности Promise решает
- Использование async/await + Bluebird для обещанияВсе
- jQuery: возвращать данные после успешного вызова ajax
Вы можете сделать это с обещаниями
router.post('/search', (req, res) => {
var collection = db.get().collection('styles');
// Create promise for "make.name" query
let firstQuery = new Promise((resolve, reject) => {
collection.distinct('make.name', (err, docs) => {
if (!err) {
resolve(docs);
} else {
reject(err);
}
});
});
// Create promise for "model" query
let secondQuery = new Promise((resolve, reject) => {
collection.distinct('model', (function (err, docs) {
if (!err) {
resolve(docs);
} else {
reject(err);
}
}))
})
// Run both queries at the same time and handle both resolve results or first reject
Promise.all([firstQuery, secondQuery])
.then((results) => {
res.send({ "make.name": results[0], "model": results[1] });
})
.catch((err) => {
// Catch error
res.send({});
});
});
Также вы можете использовать деструктурирование в таких функциях обратного вызова:
Promise.all([firstQuery, secondQuery])
.then(([makeName, model]) => res.send({ "make.name": makeName, model }))
UPD: если у вас есть куча коллекций для запроса, вы можете создать массив имен коллекций, сопоставить его с запросами на обещание и обработать, например, с помощью Promise.all
let collections = ["firstCollection", "secondCollection", "nCollection"];
let promises = collections.map((collectionName) => {
return new Promise((resolve, reject) => {
collection.distinct(collectionName, (err, docs) => {
if (!err) {
resolve(docs)
} else {
reject(err);
}
});
})
});
Promise.all(promises)
.then(results => {
// Do what you want to do
})
.catch(error => {
// or catch
});