How to "transfer" variables down the promise chain in node express router? - javascript

I am trying to make two separate DB calls in a promise chain, although for testing purposes, the first call is replaced by a simple string that gets passed along.
My problem is that I can't access the variable msg in my second promise (where I try to set context.foo = msg.
router.route("/")
.get(function(request, response) {
var session = request.session;
return new Promise(function(resolve, reject) {
resolve("h!");
}).then(function(msg){
return new Promise(function(resolve, reject) {
Snippet.find({}, function(error, data) {
let context = {
snippets: data.map(function(snippet) {
return {
name: snippet.name,
snippet: snippet.snippet,
createdAt: snippet.createdAt,
user: snippet.user,
id: snippet._id
};
}),
foo: msg
};
resolve(context);
});
});
}).then(function(context){
response.render("start/index", context);
}).catch(function(err){
response.end(err);
});
});
Another attempt, here trying to bind the router to the promise...
router.route("/")
.get(function(request, response) {
var session = request.session;
return new Promise(function(resolve, reject) {
resolve("hi!");
}).then(function(msg){
router.msg = msg;
return new Promise(function(resolve, reject) {
Snippet.find({}, function(error, data) {
let context = {
snippets: data.map(function(snippet) {
return {
name: snippet.name,
snippet: snippet.snippet,
createdAt: snippet.createdAt,
user: snippet.user,
id: snippet._id
};
}),
foo: this.msg
};
resolve(context);
}.bind(router));
});
A third attempt...
router.route("/")
.get(function(request, response) {
var session = request.session;
return new Promise(function(resolve, reject) {
context.msg = "hi!";
resolve(context);
}).then(function(context){
return new Promise(function(resolve, reject) {
Snippet.find({}, function(error, data) {
context.snippets = {
snippets: data.map(function(snippet) {
return {
name: snippet.name,
snippet: snippet.snippet,
createdAt: snippet.createdAt,
user: snippet.user,
id: snippet._id
};
}),
};
resolve(context);
});
});
}).then(function(context){
response.render("start/index", context);
}).catch(function(err){
response.end(err);
});
});
So the basic problem is always, how can I "inject" or make use of a variable inside the Promise scope, when I have no surrounding object, no "this" to attaach it to =)

Using bluebird's powerful Promise.coroutine():
router.route("/")
.get(Promise.coroutine(function*(request, response) {
try {
const dbResult1 = yield getValueFromDatabaseAsync(/* w/e */);
const dbResult2 = yield getValueFromDatabaseAsync(/* w/e, can be dbResult1 */);
const context = {...}; // use dbResult1 and dbResult2 here normally
response.render("start/index", context);
} catch (err) {
response.end(err);
}
}));

Related

How do I consume this second promise?

Here's the code
console.log("Before");
const p = getUser(1);
p.then(user => {
console.log(user);
getRepositories(user.github);
}).then(repos => {
console.log(repos);
});
console.log("after");
function getUser(id) {
return new Promise(function(resolve, reject) {
setTimeout(() => {
console.log("Calling a database " + id);
resolve({ id: id, github: "Zain" });
}, 2000);
});
}
function getRepositories(username) {
return new Promise(function(resolve, reject) {
setTimeout(() => {
console.log(`Calling Api for ${username}`);
resolve(["repo1", "repo2", "repo3"]);
}, 2000);
});
}
I'm having trouble with consuming the promise returned by getRepositories() function. Above is my implementation but it doesn't work and returns undefined (instead of the array [repo1, repo2, repo3]).
Here's the output:
I want the array to return after logging "Calling Api for Zain" but the undefined is shown before it, but I don't know why, so I need help regarding this.
You need a return statement in your first .then:
p.then(user => {
console.log(user);
return getRepositories(user.github);
}).then(repos => {
console.log(repos);
});
There’s a special syntax to work with promises in a more comfortable fashion, called “async/await”. It’s surprisingly easy to understand and use.
async function init() {
console.log("Before");
const user = await getUser(1);
console.log(user);
const repos = await getRepositories(user.github);
console.log(repos);
console.log("after");
}
init();
async function getUser(id) {
return new Promise(function(resolve, reject) {
setTimeout(() => {
console.log("Calling a database " + id);
resolve({ id: id, github: "Zain" });
}, 2000);
});
}
async function getRepositories(username) {
return new Promise(function(resolve, reject) {
setTimeout(() => {
console.log(`Calling Api for ${username}`);
resolve(["repo1", "repo2", "repo3"]);
}, 2000);
});
}
For more information: https://javascript.info/async-await

How to use json response to create another URL. Nodejs

I have the question, below code:
The problem is:
How can I send each line from response promiseGetCitiesData to promiseGetInformationDataPerCity.
Can I do it in one async.each functions?
Now, I created multiple Promise functions. One general function, which one start the program - getDataAndCloseDb().
Also I used async.each to call promise function with array parameter - locationArray.
Now, I would like to send each line from json response to next promise function (create get url), and collect the general response.
const MongoClient = require("mongodb").MongoClient;
const request = require("request");
const async = require("async");
var locationsArray = [
'location1',
'location2',
'location3'
];
function promiseConnectToDatabase(urldb) {
return new Promise(function(resolve, reject) {
MongoClient.connect(urldb, (err, db) => {
if (err) {
console.log("MongoDb connection error.");
reject(err);
}
console.log("Connected to MongoDb.");
resolve(db);
});
});
}
function promiseGetCitiesData(location) {
return new Promise(function(resolve, reject) {
request({
url: `https://example.com/${location}`,
json: true
}, (error, response, body) => {
if (error) {
console.log("Error connection to url.");
reject();
}
console.log("location: " + location);
console.log({location: location, cities: body.result.cities});
resolve({location: location, cities: body.result.cities});
});
});
}
/*
Example response from promiseGetCitiesData:
Location: location1
{ location: 'location1',
cities:
[ 'information1',
'information2',
'information3',
'information4'' ] }
*/
function promiseGetInformationDataPerCity(location, cities) {
return new Promise(function(resolve, reject) {
request({
url: `https://example.com/${location}/${cities}`,
//f.e https://example.com/location1/information1 etc.
json: true
}, (error, response, information) => {
if (error) {
console.log("Error connection to url.");
reject();
}
console.log(information);
resolve(information);
});
});
}
function promiseSaveDataToDatabase(db, body) {
return new Promise(function(resolve, reject) {
db.collection("testlocation").insert(body, function(dbError) {
if (dbError) {
reject(dbError);
}
resolve()
});
});
}
function promiseDisconnectDatabase(db) {
return new Promise(function(resolve, reject) {
db.close((err) => {
if (err) {
console.log("MongoDb disconnect error.");
reject(err);
}
console.log("MongoDb disconnected.");
resolve();
});
});
}
function promiseProvideDataFromEach(locationsArray, db) {
return new Promise(function(resolve, reject) {
async.each(locationsArray, function(loc, locProcessedCb) {
promiseGetcitiesData(loc).then(function(resultscities) {
promiseGetInformationDataPerCity(loc, resultscities).then(function(resultDetails) {
promiseSaveDataToDatabase(db, resultDetails).then(function() {});
locProcessedCb();
});
});
}, function(err) {
if (err) {
locProcessedCb(err);
reject(err);
}
console.log("All locations have been processed.");
resolve();
});
});
}
function getDataAndCloseDb() {
return new Promise(function(resolve, reject) {
promiseConnectToDatabase("mongodb://127.0.0.1:27017/testApp").then(function(db) {
promiseProvideDataFromEach(locationsArray, db).then(function() {
promiseDisconnectDatabase(db).then(function() {});
});
});
});
}
getDataAndCloseDb();
I think this is a lot simpler than the code in the question makes it appear. In particular, new Promise(...) can be completely avoided by :
using require('async-request') instead of require('request').
allowing MongoDb methods to return Promise, as many of them will do if no callback is passed.
Also
by using the Promise.all(array.map(...)) pattern the need for require('async') disappears.
https://stackoverflow.com/a/28915678/3478010 - provides a great little reusable disposer utility, which is useful here.
Remember to return a promise/value from every .then() callback that is itself asynchronous and/or should deliver data.
With some guesswork, I think you want something like this :
const MongoClient = require('mongodb').MongoClient;
const request = require('async-request'); // just like `request()` but returns a promise
var locationsArray = [
'location1',
'location2',
'location3'
];
function promiseGetCitiesData(loc) {
return request({
url: `https://example.com/${loc}`,
json: true
}).then(body => body.result.cities);
}
function promiseGetInformationDataPerCity(loc, cities) {
return Promise.all(cities.map(city => {
return request({
'url': `https://example.com/${loc}/${city}`,
'json': true
}).then(cityInfo => ({ 'name':city, 'info':cityInfo }));
}));
}
function promiseProvideDataFromEach(locationsArray, db) {
return Promise.all(locationsArray.map(loc => {
return promiseGetCitiesData(loc)
.then(cities => promiseGetInformationDataPerCity(loc, cities)
.then(citiesWithCityInfo => ({ 'location':loc, 'cities':citiesWithCityInfo }));
}))
.then(resultDetails => db.collection('testlocation').insertMany(resultDetails));
}
// disposer utility - credit: https://stackoverflow.com/a/28915678/3478010
function withDb(work) {
var _db;
return MongoClient.connect("mongodb://127.0.0.1:27017/testApp")
.then((db) => {
_db = db; // keep reference
return work(db); // perform work on db
}).finally(() => {
if (_db)
_db.close();
});
}
withDb(db => promiseProvideDataFromEach(locationsArray, db))
.then(() => {
// connection released here
});
The guesswork centres mainly around what is to be inserted at db.collection('testlocation').insertMany(resultDetails). The code in the question gives no more than a clue. My attempt seems reasonable but may not be exactly what you want. Be prepared to make some changes in promiseProvideDataFromEach() and promiseGetInformationDataPerCity().
you can do something like this. Its a simpler code but I think you can map it to your current code.
const Promise = require('bluebird')
const cities = ['citya', 'cityb', 'cityc']
function resolveCities() {
return new Promise(function(resolve, reject) {
resolve(cities)
})
}
function logCity(city) {
console.log('city ', city)
}
return resolveCities()
.then(function(cities) {
return Promise.mapSeries(cities, function(city) {
logCity(city);
});
})

Merge two arrays outside their functions using Promise.all

I'm trying to merge two arrays by their id (event.id) into one array. I faced with a problem, I don't understand how to merge these arrays outside their functions. I know that there is a way to do it using Promise.all, but don't know how to do it. Thank you for your time.
function getpinnacle() {
return new Promise(function (resolve, reject) {
var options = {sportId: 29};
pinnacle.getFixtures(options, function(err, response, body) {
if (err) throw new Error(err);
var pinnFixtures = [];
body.league.forEach(function(leagues){
leagues.events.forEach(function(event){
if (event.status == 'O'){
pinnFixtures.push({
'id': event.id,
'homeTeamName': event.home,
'awayTeamName': event.away
});
};
});
});
resolve(pinnFixtures);
});
var options = {sportId: 29, oddsFormat: "DECIMAL"};
pinnacle.getOdds(options, function(err, response, body) {
if (err) throw new Error(err);
var pinnOdds = [];
body.leagues.forEach(function(league){
league.events.forEach(function(event){
event.periods.forEach(function(period){
if (period.moneyline !== undefined) {
pinnOdds.push({
'id': event.id,
'homeTeamOdds': period.moneyline.home,
'drawOdds': period.moneyline.draw,
'awayTeamOdds': period.moneyline.away
});
};
});
});
});
resolve(pinnOdds);
});
});
}
module.exports = getpinnacle;
In order to have a context where you have both arrays available to you, you can wrap each callback in a promise, and then use Promise.all() to wait for the results:
function getpinnacle() {
var options = {sportId: 29};
const fixtures = new Promise(function(resolve, reject){
pinnacle.getFixtures(options, function(err, response, body) {
if (err) throw new Error(err);
var pinnFixtures = [];
body.league.forEach(function(leagues){
leagues.events.forEach(function(event){
if (event.status == 'O'){
pinnFixtures.push({
'id': event.id,
'homeTeamName': event.home,
'awayTeamName': event.away
});
};
});
});
resolve(pinnFixtures);
});
});
var options = {sportId: 29, oddsFormat: "DECIMAL"};
const odds = new Promise(function(resolve, reject){
pinnacle.getOdds(options, function(err, response, body) {
if (err) throw new Error(err);
var pinnOdds = [];
body.leagues.forEach(function(league){
league.events.forEach(function(event){
event.periods.forEach(function(period){
if (period.moneyline !== undefined) {
pinnOdds.push({
'id': event.id,
'homeTeamOdds': period.moneyline.home,
'drawOdds': period.moneyline.draw,
'awayTeamOdds': period.moneyline.away
});
};
});
});
});
resolve(pinnOdds);
});
});
return Promise.all([fixtures, odds]);
}
After that you can call getPinnacle().then(function(results){...}) where results is an array [fixtures, odds].
What I would do to refactor your code is to break getFixtures and getOdds into two separate functions, each of which returns a promise. Then getPinnacle would return Promise.all(getFixtures(), getOdds())
you may use promise.all like this
function getpinnacle() {
var promises = [];
var fixturePromise = new Promise( (resolve, reject) => {
///add your code here and resolve from inside you getFixture method
});
promises.push(fixturePromise);
var oddPromise = new Promise( (resolve, reject) => {
//add your getOdds method here and resolve this promise too
});
promise.push(oddPromise);
Promise.all(promises).then( (response) => {
var fixtureResponse = response[0];
var oddResponse = response[1];
//add your extra logic here
});
}

Promise and node making variables available in different functions

I have three functions, I wish to make the variable from function one and two available in function three.
Function one
Bellow in function one I am trying to include that variable emailUser in resolve to use it in a third function.
var firstMethod = function() {
var promise = new Promise(function(resolve, reject){
setTimeout(function() {
app.post('/api/data', function (req, res) {
console.log(req.body);
var emailUser = req.body.email;
res.send(emailUser);
});
console.log('first method completed');
resolve({data: emailUser });
}, 2000);
});
return promise;
};
Second function
This function I am trying to pass api_key for use in the third function.
var secondMethod = function(someStuff) {
var promise = new Promise(function(resolve, reject){
setTimeout(function() {
nodePardot.PardotAPI({
userKey: 34535345,
email: fsf#dd.com,
password: fd3sv34f,
DEBUG: true
}, function (err, client) {
if (err) {
// Authentication failed
console.error("Authentication Failed", err)
} else {
// Authentication successful
var api_key = client.apiKey;
console.log("Authentication successful !", api_key);
}
});
console.log('second method completed');
resolve({newData: api_key});
}, 2000);
});
return promise;
};
Third function
This function I would like to access the variables from function one and two. I have included a console log so that i may see the variables printed ion my console.
I would also like to access these functions for use in this third function/ method.
var thirdMethod= function() {
var promise = new Promise(function(resolve, reject){
console.log('show both functions', emailUser, api_key);
}, 3000);
});
return promise;
};
firstMethod()
.then(secondMethod)
.then(thirdMethod);
You need to resolve inside the function body when the API call has get response.
var firstMethod = function() {
var promise = new Promise(function(resolve, reject){
setTimeout(function() {
app.post('/api/data', function (req, res) {
console.log(req.body);
var emailUser = req.body.email;
res.send(emailUser);
//resolve when get the response
resolve({data: emailUser });
});
}, 2000);
});
return promise;
};
You must need to resolve or reject when error. Here I resolve the error and set api_key as empty.
var secondMethod = function(someStuff) {
var promise = new Promise(function(resolve, reject){
setTimeout(function() {
nodePardot.PardotAPI({
userKey: 34535345,
email: fsf#dd.com,
password: fd3sv34f,
DEBUG: true
}, function (err, client) {
if (err) {
// Authentication failed
console.error("Authentication Failed", err);
resolve({newData: ''});
} else {
// Authentication successful
var api_key = client.apiKey;
console.log("Authentication successful !", api_key);
resolve({newData: api_key});
}
});
}, 2000);
});
return promise;
};
function thirdMethod(result) {
console.log('show both functions', result[0].data, result[1].newData);
};
Promise.all([firstMethod(), secondMethod()])
.then(thirdMethod);
For Reference
var firstMethod = function() {
var promise = new Promise(function(resolve, reject) {
setTimeout(function() {
//resolve when get the response
resolve({
data: "a"
});
});
}, 2000);
return promise;
};
var secondMethod = function() {
var promise = new Promise(function(resolve, reject) {
setTimeout(function() {
//resolve when get the response
resolve({
data: "b"
});
});
}, 2000);
return promise;
};
var thirdMethod = function(result) {
console.log(result[0].data, result[1].data);
};
Promise.all([firstMethod(), secondMethod()]).then(thirdMethod);
Output:
a b
First you need to fix firstMethod and secondMethod. In firstMethod the emailUser variable is only set inside the callback function, it does not exist where you try to use it to resolve the promise. You need to move the promise resolution inside the callback.
Likewise in secondMethod the variable api_key only exists in the callback, and then only if the function succeeded. You need to put calls to resolve() and reject() inside that callback and remove the resolve() outside.
Once you have done that you should be resolving both promises with the correct values and your code can be:
function thirdMethod([emailUSer, api_key]) {
console.log('show both functions', emailUser, api_key);
};
Promise.all([firstMethod(), secondMethod()])
.then(thirdMethod);
Note that as thirdMethod is only called by then() you don't need to create a Promise unless you have something asynchronous to do: anything it returns will be automatically wrapped in a Promise for you.
your first promise resolves
{data: emailUser }
therefore somestuff in the second promise would be that
in your second method, you could resolve
{data: somestuff.data, newData: api_key}
then the third method can be written
var thirdMethod= function(values) {
and it would have the data from the first two promises
Overall, your code can be written (ES2016+)
var firstMethod = () => new Promise((resolve, reject) => setTimeout(() => app.post('/api/data', (req, res) => {
console.log(req.body);
var emailUser = req.body.email;
res.send(emailUser);
resolve({ emailUser });
}), 2000));
var secondMethod = ({ emailUser }) => new Promise((resolve, reject) => setTimeout(() => nodePardot.PardotAPI({
userKey: 34535345,
email: 'fsf#dd.com',
password: 'fd3sv34f',
DEBUG: true
}, (err, client) => {
if (err) {
// Authentication failed
console.error("Authentication Failed", err);
reject(err);
} else {
// Authentication successful
var api_key = client.apiKey;
console.log("Authentication successful !", api_key);
resolve({ emailUser, api_key });
}
}), 2000));
var thirdMethod = ({ emailUser, api_key }) => new Promise((resolve, reject) => setTimeout(() => {
console.log('show both functions', emailUser, api_key);
resolve('done');
}, 3000));
firstMethod().then(secondMethod).then(thirdMethod);

How to asynchronously update AngularJS state using value inside of Promise

How can i access a returned value from then() in JavaScript?
I have the following function:
function getResult(){
var promise = _myService.getAge();
var getResultPromise = promise.then(function(age){
return age;
})
return getResultPromise; //How do i get the age (value) here? *see image*
}
Or how would i access the value in the $$state object below?
How about this:
//The simple solution, this is how to use a Promise:
let _myService = {
getAge: function() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(3)
}, 500);
})
}
};
function getResult() {
_myService.getAge().then(function(age) {
console.log(age);
});
}
getResult();
//OR, maybe closer to OP's intended implementation, put the value in an outer state:
let outerState = {
age: 0
};
setTimeout(function() {
console.log(outerState)
}, 450);
let _myService2 = {
getAge: function() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(8)
}, 750);
})
}
};
function addAgeToOuterState() {
pState = new Promise(function(resolve, reject) {
_myService2.getAge().then(function(newAge) {
outerState.age = newAge;
resolve(outerState);
});
});
pState.then(function() {
console.log(outerState);
});
return pState;
}
addAgeToOuterState()
//for fun
.then(function(outer) {
console.log(outer); //state is still here
});

Categories