Using promises in my mean stack app - javascript

I've created a controller that does a bing search based on the user's input into the url. Based on my results from doing a console.log the controller is working correctly and I have set that variable to return. In the route file the information is not displaying to the page. I thought it might be an asynchronous issue so I am trying to use promises to make sure the controller has returned before it tries to do the res.json but I'm not very familiar with promises so my syntax might be off or I might be going about this the wrong way. Will someone take a look at this syntax and see if there is an issue.Currently only an empty object is displaying on the page.
app.route('/imagesearch/:keyword')
.get(function (req, res) {
var resObj = [];
resObj = new Promise (function(resolve, reject){
resolve(bingSearchHandler.findImages(req.params));
});
resObj.then(res.json(resObj));
});
//BINGSEARCHHANDLER
'use strict';
var bingAPPID = 'fwHyQAoJMJYmK8L4a3dIV2GAEUfXAlFRjCnBx0YbfPE=';
var Search = require('bing.search');
var util = require('util');
var search = new Search(bingAPPID);
function bingSearchHandler () {
this.findImages = function(userInput){
var keyword = userInput.keyword;
search.images(keyword,
{top: 10},
function(err, results) {
if(err)
{
console.log(err);
}
else
{
var resArr = [];
(util.inspect(results,
{colors: true, depth: null}));
for(var i=0;i<results.length;i++)
{
var tempObj = {};
tempObj.url = results[i].url;
tempObj.snippet = results[i].title;
tempObj.thumbnail = results[i].thumbnail.url;
tempObj.context = results[i].sourceUrl;
resArr.push(tempObj);
}
console.log(resArr);
return resArr;
}
}
);
}
}
module.exports = bingSearchHandler;

Could you please try this code? Here you have the bing.search documentation, https://www.npmjs.com/package/bing.search Always try to use callbacks instead of promises in NodeJs, remember that the first parameter of a callback is always the error (if there is any), then the response
app.route('/imagesearch/:keyword')
.get(function (req, res) {
bingSearchHandler.findImages(req.params, function (err, response) {
if (err) return res.status(400)
res.json(response)
})
});
//BINGSEARCHHANDLER
'use strict';
var bingAPPID = 'fwHyQAoJMJYmK8L4a3dIV2GAEUfXAlFRjCnBx0YbfPE=';
var Search = require('bing.search');
var util = require('util');
var search = new Search(bingAPPID);
function bingSearchHandler () {
this.findImages = function(userInput, callback){
var keyword = userInput.keyword;
search.images(keyword,
{top: 10},
function(err, results) {
if(err) callback(err)
else
{
var resArr = [];
(util.inspect(results,
{colors: true, depth: null}));
for(var i=0;i<results.length;i++)
{
var tempObj = {};
tempObj.url = results[i].url;
tempObj.snippet = results[i].title;
tempObj.thumbnail = results[i].thumbnail.url;
tempObj.context = results[i].sourceUrl;
resArr.push(tempObj);
}
console.log(resArr);
return callback(null, resArr);
}
}
);
}
}
module.exports = bingSearchHandler;

Something like this should work.
Using callbacks
app.route('/imagesearch/:keyword')
.get(function (req, res) {
// Make the async request, pass the callback function
bingSearchHandler.findImages(req.params, (error, response) => {
if (error === null) {
res.json(response);
}
});
});
Additionally, you'll need to rework your findImages function.
this.findImages = (userInput, callback) => {
var keyword = userInput.keyword;
search.images(keyword, {top: 10}, function (err, results) {
if (err) {
callback(err);
}
else {
var resArr = [];
util.inspect(results, {colors: true, depth: null});
for(var i = 0; i < results.length; i++) {
var tempObj = {};
tempObj.url = results[i].url;
tempObj.snippet = results[i].title;
tempObj.thumbnail = results[i].thumbnail.url;
tempObj.context = results[i].sourceUrl;
resArr.push(tempObj);
}
callback(null, resArr);
}
});
}
Using promises
app.route('/imagesearch/:keyword')
.get(function (req, res) {
// Make the async request, pass the callback function
bingSearchHandler.findImages(req.params).then(response =>
res.json(response);
});
});
// Images function
this.findImages = (userInput) => {
return new Promise((resolve, reject) => {
var keyword = userInput.keyword;
search.images(keyword, {top: 10}, function (err, results) {
if (err && typeof reject === 'function') {
reject(err);
}
else {
var resArr = [];
util.inspect(results, {colors: true, depth: null});
for(var i = 0; i < results.length; i++) {
var tempObj = {};
tempObj.url = results[i].url;
tempObj.snippet = results[i].title;
tempObj.thumbnail = results[i].thumbnail.url;
tempObj.context = results[i].sourceUrl;
resArr.push(tempObj);
}
if (typeof resolve === 'function') {
resolve(resArr);
}
}
});
});
}

Related

MongoDB Find queries slow while updating/inserting schema

I'm doing a big loop once a day - which updating existing documents in the database (and also inserting new documents).
this loop get executed in a separate server ( prevents from the main server to be slow ), but the main problem is that all the find queries on the Data base (while the loop is executed) are very slow (the loop slows it down significantly).
This is a very big issue in my website ( this loop must be executed once a day ) and iv'e been trying to find a solution online - but i couldn't manage to find something.
Is there any way to prevent the find queries from being so slow while inserting/updating the database??
uploadProductsManually = async (name, products, map, valuesMap) => {
return new Promise(async function (resolve, reject) {
const company = await Company.findOne({ name }).exec();
if (!company) return reject(new errors.NotFound("Company not found"));
const rows = products;
const parsedRows = [];
const findCorrectKey = (key) => {
const correctKey = key.trim();
if (productFields[correctKey]) return productFields[correctKey];
const category = map.find((item) => {
return item.options.some((option) => {
return option.trim().toLowerCase() === correctKey.toLowerCase();
});
});
const categoryName = category && category.name;
return productFields[categoryName];
};
const hashProductValues = (product) => {
let valueToHash;
if (product.productId) {
valueToHash = product.productId;
} else if (product.certificateId) {
valueToHash = product.certificateId;
} else {
valueToHash = JSON.stringify(
product.size + product.color
);
}
return base64encode(valueToHash);
};
rows.forEach(function (row, i) {
var newProduct = {};
for (var key in row) {
var val = row[key];
if (val) {
let normalizedKey = findCorrectKey(key);
if (normalizedKey) {
newProduct[normalizedKey] = val;
}
let normalizedValue = normalizeValue(normalizedKey, val,valuesMap);
newProduct[normalizedKey] = normalizedValue;
}
}
newProduct.channels = [];
if (newProduct.productId) {
parsedRows.push(newProduct);
}
});
fetchProducts();
function fetchProducts() {
Product.find({ company: company._id }).exec(function (err, products) {
if (err) console.log(err);
var map = {};
if (products) {
products.forEach(function (product) {
const productIdentifier = hashProductValues(product);
map[productIdentifier] = product;
if (product.productStatus == "manual") {
// product.isAvailable = false;
// product.save();
} else {
product.status = "deleted";
product.save();
}
});
}
mergeData(map);
});
}
async function mergeData(map) {
let created = 0;
let updated = 0;
let manual = 0;
async.each(
parsedRows,
function (row, callback) {
const productIdentifier = hashProductValues(row);
let product = map[productIdentifier];
if (product) {
map[productIdentifier] = undefined;
Product.findByIdAndUpdate(id, { $set: updatedProduct }, function (
err,
updatedProd
) {
if (err) {
// errors.push(productIdentifier);
console.log("err is:", err);
}
updated++;
callback();
});
} else {
row = new Product(row);
row.save(function (err) {
if (err) {
// errors.push(productIdentifier);
console.log(err);
}
created++;
callback();
});
}
},
(err) => {
if (err) return reject(err);
Company.findByIdAndUpdate(
company._id,
{ lastUpdate: new Date() },
function (err, comp) {
if (err) console.log(err);
}
);
console.log(
`Created: ${created}\nUpdated: ${updated} \manual: ${manual}`
);
resolve({
created,
updated,
manual,
errors,
});
}
);
}
});
};

Iterating through list and make sequential network calls

How do i iterate through a list and make sequential network calls using a sdk?
I am trying to use Coinbase's Node sdk and get the first 10 transactions for all accounts.
I have a list of accounts, and i iterating through them and calling client.account, client.transactions, and client.transactions(pagination). And im adding the results to an array of transactions and returning that array.
I couldn't get this to work with async/await or request-promises.
Any ideas?
https://developers.coinbase.com/api/v2#transactions
var rp = require('request-promise');
var coinbase = require('coinbase');
var client = new coinbase.Client({ 'apiKey': 'keyStuff', 'apiSecret': 'secretStuff' });
var accountList = ['acct1','acct2','acct3',];
var transactionList = [];
try {
let ps = [];
accountList.forEach(acctId => {
var account = client.getAccount(accountId, null);
ps.push(rp(account));
});
Promise.all(ps)
.then(responses => {
for (var i = 0; i < responses.length; i++) {
var result = responses[i];
rp(result.account.getTransactions(null))
.then(res => {
res.pagination = 10;
return rp(result.account.getTransactions(null, res.pagination));
}).catch(err => console.log(err))
.then(txns => {
try {
if (txns.length > 0) {
txns.forEach(function(txn) {
var transaction = {
"trade_type": "",
"price": ""
};
transaction.trade_type = txn.type;
transaction.price = txn.native_amount.amount;
transactionList.push(transaction);
});
}
}
catch (err) {
console.log(err);
}
});
}
}).catch(err => console.log(err));
return transactionList;
//-------------------------------------------------------------------
// if (accountList.length > 0) {
// for (let acctId of accountList) {
// console.log("account id: " + acctId);
// await delayTransactions(acctId);
// }
// console.log("got here last");
// return transactionList;
// }
}
catch (error) {
console.log(error);
}
The commented-out delay method has nested async calls like this:
await client.getAccount(accountId, async function(err, account) {
if (err) {
console.log(err);
}
else {
await account.getTransactions(null, async function(err, txns, pagination) {
.
.
.
Solved it by using async/await and promises. awaiting the coinbase methods wouldn't work because they aren't async functions (surprise!).
function delayedMethod() {
new Promise(resolve => {
client.getAccount(accountId, async function(err, account) {
if (err) {
console.log(err);
}
else {
account.getTransactions(null, async function(err, txns, pagination) {
.
.
.
resolve();
});
}

why my array is empty?

i can not figure out why my array is empty while the console is showing output:
I'm fetching data from dynamodb and trying to make a modified response json array i can print the values of dynamodb response but when i push it into array the returned array is empty here is my code.
Here is Util.js file
const dynamo = require('./dynamo.js')
var myjson = [];
exports.myjson = [];
exports.maketimetabledata = function(callback) {
var table = "timetable";
for (i = 1; i <= 2; i++) {
dynamo.docClient.get({
TableName: table,
Key: {
"id": i
}
}, function(err, data) {
if (err) {
console.error("Unable to read item. Error JSON:", JSON.stringify(err, null, 2));
return err;
} else {
dynamores = JSON.parse(JSON.stringify(data));
myjson.push(i);
}
});
console.log(myjson);
}
callback("done");
};
and here is my partial index file:
app.get('/', (req, res) => {
var myjson = [];
util.maketimetabledata(function(returnvalue) {
if (returnvalue) {
console.log(util.myjson);
}
});
});
output :
[] [] []
undefined
{ response from dynamo db for debugging purpose}
Assuming dynamo.docClient.get() returns a Promise, You can use Promise.all() to wait for all the promise to complete then invoke the callback method.
//Use Promise.all()
exports.maketimetabledata = function (callback) {
var table = "timetable";
for (i = 1; i <= 2; i++) {
var table = "timetable";
var promiseArray = [];
for (i = 1; i <= 2; i++) {
var promise = dynamo.docClient.get({
//Your code
});
promiseArray.push(promise)
}
console.log(myjson);
}
Promise.all(promiseArray).then(function () {
callback(myjson);
});
};
//Usage
util.maketimetabledata(function (myjson) {
if (returnvalue) {
console.log(myjson);
}
});
If the method is not returning Promise
var promise = new Promise(function (resolve, reject) {
dynamo.docClient.get({
TableName: table,
Key: {
"id": i
}
}, function (err, data) {
if (err) {
console.error("Unable to read item. Error JSON:", JSON.stringify(err, null, 2));
reject();
return err;
} else {
dynamores = JSON.parse(JSON.stringify(data));
myjson.push(i);
resolve();
}
});
});

mongoose-random response 500

I've just opened a nodeJS code trying to get random documents from my mongoose collection using mongoose-random, but for any reason when I call to findRandom() method, it responses a 500.
test.js
Here bellow I paste my code:
var mongoose = require('mongoose');
var random = require('mongoose-random');
var Promise = require('bluebird');
mongoose.Promise = Promise;
var TestSchema = new mongoose.Schema({
_id: {
type: Number,
default: 0
}
});
TestSchema.plugin(random, {path: 'r'});
TestSchema.statics = {
start: function (value) {
var array = [], i = 1;
for (i; i < value; i += 1) {
array.push({ _id: i });
}
return this.create(array);
},
getRandom: function () {
return new Promise(function(resolve, reject) {
TestSchema.findRandom().limit(10).exec(function (err, songs) {
if (err) {
reject(err);
} else {
resolve(songs);
}
});
});
}
};
module.exports = mongoose.model('TestSchema', TestSchema);
routes.js
var router = require('express').Router();
var path = require('path');
var Promise = require('bluebird');
var request = require('request');
var test = require('./models/test.js');
router.get('/fill', function (req, res) {
test.start(40)
.then(function () {
res.status(200).send('You can start your hack :)');
})
.catch(function (error) {
res.status(400).send(error);
});
});
router.get('/vote', function (req, res) {
test.getRandom()
.then(function (data) {
res.status(200).send(data);
})
.catch(function (error) {
res.status(400).send(error);
});
});
module.exports = router;
Reading other post here as a solution purposes to use syncRandom() method, but that doesn't work for me. Neither using random()
Any help? Thanks in advice.
UPDATE
Digging more into the issue, I've realiced my model TestSchema, which should contain mongoose-random methods is being overrided, so I only have my statics methods.
Don't set statics to a new object, just add methods to it:
TestSchema.statics.start = function (value) {
var array = [], i = 1;
for (i; i < value; i += 1) {
array.push({ _id: i });
}
return this.create(array);
};
TestSchema.statics.getRandom = function () {
return new Promise(function(resolve, reject) {
TestSchema.findRandom().limit(10).exec(function (err, songs) {
if (err) {
reject(err);
} else {
resolve(songs);
}
});
});
};
However, if your MongoDB server is at least 3.2, you're probably better off using the built-in $sample pipeline operator instead of a plugin to select random documents.
test.aggregate([{$sample: 10}], callback);

Cant send headers after already sent?

What does that even mean in terms of the following code, the res.send works fine however in my console i get the following message:
http.js:689
throw new Error('Can\'t set headers after they are sent.');
app.get('/summoner/:summonerName', function(req, res) {
lolapi.Summoner.getByName(req.params.summonerName, function(err, obj) {
var options = {
beginIndex: 0,
endIndex: 1
};
lolapi.MatchList.getBySummonerId(obj['savisaar2'].id, options, function(err, matches) {
var gameMatches = matches.matches;
for(var i = 0; i < gameMatches.length; i++) {
lolapi.Match.get(gameMatches[i].matchId, function(err, games) {
res.send({profile : obj, matchHistory : games});
});
}
});
});
});
As I explained in my comments, you are calling res.send() inside a for loop which means you are calling it more than once. You can only call it once per request. That is why you are seeing the error message in the console.
It is not clear exactly what your code really wants to do, but if the desire is to collect all the results into an array and send them all as the response, then you can do that like this:
app.get('/summoner/:summonerName', function (req, res) {
lolapi.Summoner.getByName(req.params.summonerName, function (err, obj) {
if (err) {
return res.status(500).end();
}
var options = {beginIndex: 0, endIndex: 1};
lolapi.MatchList.getBySummonerId(obj['savisaar2'].id, options, function (err, matches) {
var gameMatches = matches.matches;
var results = [];
for (var i = 0; i < gameMatches.length; i++) {
lolapi.Match.get(gameMatches[i].matchId, function (err, games) {
if (err) {
return res.status(500).end();
}
results.push({profile: obj, matchHistory: games});
// if all results are done, then send response
if (results.length === gameMatches.length) {
res.json(results);
}
});
}
});
});
});
Note: I've also added rudimentary error handling.
If you want the results in the particular order that you requested then, then you can add a little more code to do that like this:
app.get('/summoner/:summonerName', function (req, res) {
lolapi.Summoner.getByName(req.params.summonerName, function (err, obj) {
if (err) {
return res.status(500).end();
}
var options = {beginIndex: 0, endIndex: 1};
lolapi.MatchList.getBySummonerId(obj['savisaar2'].id, options, function (err, matches) {
var gameMatches = matches.matches;
var results = new Array(gameMatches.length);
var cntr = 0;
for (var i = 0; i < gameMatches.length; i++) {
(function(index) {
lolapi.Match.get(gameMatches[i].matchId, function (err, games) {
if (err) {
return res.status(500).end();
}
++cntr;
results[index] = {profile: obj, matchHistory: games};
// if all results are done, then send response
if (cntr === gameMatches.length) {
res.json(results);
}
});
})(i);
}
});
});
});
Since Promises are now standard in 2016, here's an idea what this could look like using the Bluebird promise library:
const Promise = require('bluebird');
Promise.promisifyAll(lolapi.Summoner);
Promise.promisifyAll(lolapi.MatchList);
Promise.promisifyAll(lolapi.Match);
app.get('/summoner/:summonerName', function (req, res) {
var main;
lolapi.Summoner.getByNameAsync(req.params.summonerName).then(function(obj) {
main = obj;
var options = {beginIndex: 0, endIndex: 1};
return lolapi.MatchList.getBySummonerIdAsync(obj['savisaar2'].id, options);
}).then(function(matches) {
var gameMatches = matches.matches;
return Promise.map(gameMatches, function(item){
return lolapi.Match.getAsync(item.matchId).then(function(games) {
return {profile: main, matchHistory: games};
});
});
}).then(function(results) {
res.json(results);
}).catch(function(err) {
res.status(500).end();
});
}

Categories

Resources