I have a function,
asdf() {
var a = fooController.getOrCreateFooByBar(param);
console.log("tryna do thing");
console.log(a); //undefined
if (!a.property) {
//blah
}
that dies. getOrCreateFooByBar does a
Model.find({phoneNumber : number}).exec()
.then(function(users) {})
and finds or creates the model, returning it at the end:
.then(function(foo) { return foo}
How can I use the result of this in asdf()? I feel like this is a fairly easy question but I am getting stuck. If I try to do a.exec() or a.then() I get 'a cannot read property of undefined' error.
The main idea about Promises (as opposed to passed callbacks) is that they are actual objects you can pass around and return.
fooController.getOrCreateFooByBar would need to return the Promise it gets from Model.find() (after all of the processing done on it). Then, you'll be able to access it in a in your asdf function.
In turn, asdf() should also return a Promise, which would make asdf() thenable as well. As long as you keep returning Promises from asynchronous functions, you can keep chaining them.
// mock, you should use the real one
const Model = { find() { return Promise.resolve('foo'); } };
function badExample() {
Model.find().then(value => doStuff(value));
}
function goodExample() {
return Model.find().then(value => doStuff(value));
}
function asdf() {
var a = badExample();
var b = goodExample();
// a.then(whatever); // error, a is undefined because badExample doesn't return anything
return b.then(whatever); // works, and asdf can be chained because it returns a promise!
}
asdf().then(valueAfterWhatever => doStuff(valueAfterWhatever));
Related
I am making a function in nodejs to read a value from the database in Firebase and return it. I read in the that way and get the right value by the console.log(), but when i made the return of the value doesn´t work properly when i call that function
function test() {
database.ref(sessionId + '/name/').once("value").then((snapshot) => {
var name = snapshot.child("respuesta").val()
console.log(name);
return name;
});
}
If someone could help me. Thanks
You're not returning anything from test, so undefined is being implicitly returned. You can return the promise like this:
function test() {
return database.ref(// everything else is identical
}
Note that this will be returning a promise that resolves to the name, not the name itself. It's impossible to return the name, because that doesn't exist yet. To interact with the eventual value of the promise, you can call .then on the promise:
test().then(name => {
// Put your code that uses the name here
})
Or you can put your code in an async function and await the promise:
async function someFunction() {
const name = await test();
// Put your code that uses the name here
}
Here is my code in TypeScript:
private getInfoSystem = () => {
this.systemInfo = {
cpuSpeed: 0 ,
totalRam: os.totalmem(),
freeRam: os.freemem(),
sysUpTime: os_utils.sysUptime(),
loadAvgMinutes: {
one: os_utils.loadavg(1),
five: os_utils.loadavg(5),
fifteen: os_utils.loadavg(15),
}
}
si.cpuCurrentSpeed().then ((data)=> {
this.systemInfo.cpuSpeed = data.avg ;
});
return this.systemInfo;
};
The property "cpuSpeed" first initialized to Zero and then I call the method cpuCurrentSpeed() which use a callback function and I try to put the value in "cpuSpeed".
The problem is that the the data of cpuCurrentSpeed() is late and the return value not include the wanted value in "cpuSpeed".
Because you are using .then on the cpuCurrentSpeed call, I assume that it's returning a Promise and not using callbacks. If it's like this, you could make your method asynchronous and await it:
private getInfoSystem = async () => {
this.systemInfo = {
cpuSpeed: 0 ,
totalRam: os.totalmem(),
freeRam: os.freemem(),
sysUpTime: os_utils.sysUptime(),
loadAvgMinutes: {
one: os_utils.loadavg(1),
five: os_utils.loadavg(5),
fifteen: os_utils.loadavg(15),
}
}
this.systemInfo.cpuSpeed = (await si.cpuCurrentSpeed()).avg;
return this.systemInfo;
};
Please note, that in this case your method also returns a Promise of the type of the value of this.systemInfo afterwards (which then needs to be awaited again by the calling method).
Otherwise, you can only rely on the returning value of Promises or callbacks inside of the callback because of it's asynchronous nature.
the console.log(workingWeekdaysVar) line; is outside the findOne's scope, and the variable was declared outside it too, yet it's giving me null ...
when i put console.log(workingWeekdaysVar); inside the findOne's scope, it does give the right output, but this is useless for me because I wanna use workingWeekdaysVar elsewhere below.
The two commented out lines are the 2nd approach i attempted to do, but it gave me an undesirable output because this whole code is inside a complicated for loop.
How can I simply pass the fetched value of workingWeekdaysVar out of the scope?
var workingWeekdaysVar = [];
buyerSupplierFisModel.findOne(filter).then(function (combo) {
workingWeekdaysVar = combo.workingWeekdays;
//server.getWorkingWeekdays = function () { return combo.workingWeekdays };
});
console.log(workingWeekdaysVar);
//console.log(server.getWorkingWeekdays());
findOne() is an asynchronous function (it returns a promise object). This means that it returns inmediately and your next code line is run (in this case, console.log(workingWeekdaysVar);. But, since the function isn't done yet, workingWeekdaysVar is empty, and it will be empty until findOne() has done its job and returned the results in the provided chained callback .then(function (combo) {....
So if you want to do anything with the results, you'll have to do it in the callback. One option to this would be to use async / await:
(async () => {
try {
const { workingWeekdaysVar } = await buyerSupplierFisModel.findOne(filter)
console.log(workingWeekdaysVar)
} catch (e) {
console.log(`Error: ${e}`);
}
})()
Re-arranging your code a bit:
let doToResponse = (combo)=>{
workingWeekdaysVar = combo.workingWeekdays;
console.log(workingWeekdaysVar);
}
buyerSupplierFisModel.findOne(filter).then(function (combo) {
doToResponse(combo)
});
good for re-usability
My personal favorite:
buyerSupplierFisModel.findOne(filter).then(combo=> {
workingWeekdaysVar = combo.workingWeekdays;
console.log(workingWeekdaysVar);
});
The important thing is keep in mind, as Miguel Calderón says.. findOne - returns a promise. At that point you have another thread with different local (Lexical?) scope
As a relative beginning in Javascript development, I'm trying to understand this problem I'm encountering in a function I've built that will be a part of the code to connect to a postgreSQL database.
In this function, I'm using the knex query builder method to check if a table exists in a remote database, and this method resolves to a boolean that indicates whether the string you specified matches with a table of the same name in the database. I've a provided a sample example of the knex syntax so people better understand the function.
knex.schema.hasTable('users').then(function(exists) {
if (!exists) {
return knex.schema.createTable('users', function(t) {
t.increments('id').primary();
t.string('first_name', 100);
t.string('last_name', 100);
t.text('bio');
});
}
});
I am trying to make my function return a boolean by using the .every Array method, which checks each array and should every index pass the condition defined, the .every method should return a true value, otherwise false. I've built a function which takes an array of schema keys, or names of tables, and passes it to the .every method. The .every method then uses the knex.schema.hasTable method to return a true or false.
My concern is that through many different configurations of the function, I have not been able to get it to return a correct value. Not only does it return an incorrect value, which may have something to do with .every, which I believe can return "truthey" values, but after defining the function, I will often get a "Function undefined" error when calling it later in the file. Here is a sample of my function - again I think it is moreso my poor understanding of how returns, promises and closures are working together, but if anyone has insight, it would be much appreciated.
var schemaTables = ['posts','users', 'misc'];
// should return Boolean
function checkTable() {
schemaTables.every(function(key) {
return dbInstance.schema.hasTable(key)
.then(function(exists) {
return exists;
});
});
}
console.log(checkTable(), 'checkTable function outside');
// console.log is returning undefined here, although in other situations,
I've seen it return true or false incorrectly.
Your function is not working properly for two reasons:
You are not returning the in the checkTable function declaration, so it will always return undefined.
You should write:
function checkTable() {
return schemaTables.every(function(key) {
return dbInstance.schema.hasTable(key)
.then(function(exists) {
return exists;
});
});
}
Anyway you will not get what you want just adding return. I'll explain why in the second point.
Array.prototype.every is expecting a truthy or falsey value syncronously but what the dbInstance.schema.hasTable returns is a Promise object (and an object, even if empty, is always truthy).
What you have to do now is checking if the tables exist asynchronously, i'll show you how:
var Promise = require("bluebird");
var schemaTables = ['posts', 'users', 'misc'];
function checkTable(tables, callback) {
// I'm mapping every table into a Promise
asyncTables = tables.map(function(table) {
return dbInstance.schema.hasTable(table)
.then(function(exists) {
if (!exists)
return Promise.reject("The table does not exists");
return Promise.resolve("The table exists");
});
});
// If all the tables exist, Promise.all return a promise that is fulfilled
// when all the items in the array are fulfilled.
// If any promise in the array rejects, the returned promise
// is rejected with the rejection reason.
Promise.all(asyncTables)
.then(function(result) {
// i pass a TRUE value to the callback if all the tables exist,
// if not i'm passing FALSE
callback(result.isFulfilled());
});
}
checkTable(schemaTables, function (result) {
// here result will be true or false, you can do whatever you want
// inside the callback with result, but it will be called ASYNCHRONOUSLY
console.log(result);
});
Notice that as i said before, you can't have a function that returns a true or false value synchronously, so the only thing you can do is passing a callback to checkTable that will execute as soon as the result is ready (when all the promises fulfill or when one of them rejects).
Or you can return Promise.all(asyncTables) and call then on checkTable it self, but i'll leave you this as exercise.
For more info about promises check:
The bluebird website
This wonderful article from Nolan Lawson
Thanks Cluk3 for the very comprehensive answer. I actually solved it myself by using the .every method in the async library. But yes, it was primarily due to both my misunderstanding regarding returns and asynchronous vs synchronous.
var checkTablesExist = function () {
// make sure that all tables exist
function checkTable(key, done) {
dbInstance.schema.hasTable(key)
.then(function(exists) {
return done(exists);
});
}
async.every(schemaTables, checkTable,
function(result) {
return result;
});
};
console.log(checkTablesExist());
// will now print true or false correctly
I'm kinda new to this q stuff and I find it pretty awesome, but there's something I still can't figure out.
I managed to run some combined promises with q.all by passing q.all an array of promises. Something like this..
var promises = [promiseOne(), promiseTwo()];
q.all(promises).then(function (results) {
res.send(results);
} );
The thing with this is that I would actually want those promises to be named, so I don't have to rely in the order of the promises.
I read somewhere that you can actually pass an object to q.all, to have the results named. So that would be something like this:
var promises = { promiseOne: promiseOne(), promiseTwo: promiseTwo() }
q.all(promises).then(function(results){
res.send(results);
});
But I guess this just doesn't work the same way as sending an array as I'm not getting the results of my promises in there. The result I get is similar to this one:
{
promiseOne: {
source: {}
},
promiseTwo: {
source: {}
}
}
So how would you go about getting named results from q.all?
One thing to note is that the amount of promises I will have in the promises array is not fixed as I get that from a GET param sent by the user to my function.
Also, inside each of my promises I have another array (or object) of promises to be resolved and whose results I would like to be named as well.
Here is another way to write the code Roamer wrote with the functionality you asked for (return an object):
Q.props = obj => {
const ps = Q.all(Object.keys(obj).map(x => Q.all([x, obj[x]])));
return ps.then(x => x.reduce((p, c) => {p[c[0]] = c[1]; return p } , {}));
};
Which would let you do:
Q.props(promises).then(o => {
o.promiseOne
});
Although you should consider using bluebird if you want all these helper functions.
Q.js appears not to offer Q.all(object), therefore you will need to map your object to an array before passing to Q.all()
Something like this will be reusable and convenient :
Q.allObj = function (obj) {
var array = Object.keys(obj).map(function(key, i) {
try {
//expect a promise
return obj[key].then(function(value) {
return {key: key, value: value};
});
}
catch(e) {
// whoops it was a value
return {key: key, value: obj[key]};
}
});
return Q.all(array);
};
Use as follows :
Q.allObj(myObj).then(function(results) {
results.forEach(function(obj) {
var name = obj.key;
var value = obj.value;
...
});
});