I am trying to understand firebase-realtime-database. I am able to fetch the data, but I do not know how to add a completion block. I mean, is there a function to check when my data fetch query is completed?
function getData() {
firebase.database().ref('SectionNames').once('value', function(names) {
names.val().forEach(function(sectionname) {
firebase.database().ref('Sections').child(sectionname).once('value').then( function(child)
//code
});
});
});
//Completion block or a method to call processData() after I get all the sections
}
function processData() {
//call this function after you get all the sections
}
Thank you very much!
The once() method also returns a promise, so you can use the usual promise handling logic (then()/catch() or try/catch).
For example:
function getData() async {
let names = await firebase.database().ref('sections').once('value');
processData(names);
}
Update: since you now updated your code to show there are multiple once calls in a loop, you can use a Promise.all for that:
function getData() {
firebase.database().ref('SectionNames').once('value', function(names) {
let promises = [];
names.val().forEach(function(sectionname) {
promises.push(
firebase.database().ref('Sections').child(sectionname).once('value')
);
});
Promise.all(promises).then((snapshots) => {
processData(snapshots);
});
});
}
Related
I simply want to load a JSON and store to a variable/array so that I could load the JSON api once and use the array to work with. Is there a way to fetch and store a JSON to a variable that I can access outside the fetch() or $.getJSON async functions?
I've come up with the code below, but I'm afraid it will load the JSON api everytime I call the function 'test'. Am I right?
function test(callback) {
$.getJSON('myURL', function (data) {
callback(data);
});
}
test(function (data) {
console.log(data);
});
If the data at myURL doesn't change, then one option is to have test cache the resolve value (or Promise), so that only one network request is made (and, for easy asynchronous use, use fetch, which returns a native Promise):
const test = (() => {
let prom = null;
return () => {
if (!prom) {
prom = fetch('myUrl').then(res => res.json());
}
return prom;
};
})();
test()
.then((data) => {
console.log(data);
});
You just need to declare the variable outside of the getJSON function:
var saved_data = null;
function test(callback) {
$.getJSON('', function (data) {
saved_data = data;
for(var i=0; i<saved_data.length; i++){
$('.list').append('<li data-id="'+i+'">'+saved_data[i].title+'</li>')
}
}, 1000);
}
test();
$(document).on('click', '.list li', function(){
console.log(saved_data[$(this).data('id')])
});
The first thing about asynchronous functions is that you have to wait for them to finish (and you don't know how long that can take), before you continue with your flow. In Javascript there are two ways to address that - using the Promise API or using a callback function. In your example you have a callback that's passed to getJSON, once that callback is fired, you will have the response data inside the saved_data variable.
If you populate the links in the callback, then you can be sure that whenever a link is clicked your saved_data variable will be available, holding the data from the API response.
JSFiddle
You can always assign the data to a variable outside of the callback. I usually do this inside of then().
const apiCall = new Promise(resolve => {
setTimeout(() => {
resolve('Data here')
}, 1000)
})
let result
apiCall.then(data => {
result = data
console.log(result)
})
And using async/await:
const apiCall = new Promise(resolve => {
setTimeout(() => {
resolve('Data here')
}, 1000)
})
const asyncFunction = async () => {
const result = await apiCall
console.log(result)
}
asyncFunction()
I get some data from mongodb using mongoose find() and perform some validation on that data, but the problem is that this function is async and it does not wait for the data to be completed and execute the next code.
and when the next code is executed it enables to perform because it has null data. then i wrote my validation logic in the async function so that when data is available only then it move to next code but on every return it sends undefined data.
function isValidObject(obj) {
schemaData.find({}, (error, data) => { // calls data from db
var contactSchema = data; // this is data i need
if(//validation fails){
return "wrong data";// every time it returns undrfined on every
// condition
}
});
}
var validationResp = isValidObject(obj);
console.log(validationResp); // output is undefined
i also used "await" to wait for the data, but in that case it return [object promise] on every return statement
use async/await
In your case:
async function isValidObject(obj) {
let data = await schemaData.find({}); // wait until it resolve promise
//do your validaion
return data;
}
isValidObject(obj).then((validationResp)=>{console.log(validationResp)});
use the then() method it return a promise
var promise1 = new Promise(function(resolve, reject) {
resolve('Success!');
});
promise1.then(function(value) {
console.log(value);
// expected output: "Success!"
});
more details at MDN Using the then method
Are you familiar with "promises".You can use ".then(callback)". It will wait until async function is executed. And you can perform your validation in the callback function
User.findById(id)
.then(function(user) {
if (!user) {
return "here you return an error";
}
return "you return OK with payload from database";
})
.catch(next);
What is happening in your case is that when you assign var validationResp = isValidObject(obj); the function isValidObject(obj) has not returned anything and has only scheduled a callback(This is a very important concept when working with callbacks). As pointed out above you need to use Promises. The below is an example of your case as to how you can use Promises.
function isValidObject(obj){
return new Promise((resolve,reject) => {
schemaData.find({}, (error, data) => { // calls data from db
if(validation fails){
reject(error)// every time it returns undrfined on every
// condition
}else{
var contactSchema = data; // this is data i need
resolve(data)// You can resolve the promise with the data directly instead
// of assigning it to a variable or you can use (resolve(contactSchema))
}
})
})
}
After this when you want to use the data you can use do something like the below code snippet.
isValidObject(obj)
.then(result => {
// Use the result object here. This is the result from the Database
})
.catch(error => {
//Handle the Error
})
I have a Cordova mobile app that stores offline data in localStorage. Recently users started getting QUOTA_EXCEEDED_ERR error because localStorage has 5MB limit. I decided to use "localForage" framework, but I noticed that it works asynchronously. Since I don't want to rewrite all my complex app wrapping into callback functions I wanted to know if there is some way to use "localForage" synchronously (wait till getItem function returns value).
Here is a code example what I am trying to do:
localforage.setItem('testKey', 'testValue', function() {
var value = getValue('testKey');
console.log(value); // here I get undefined, but I want to get a value
});
function getValue(key) { // I want this function to return value
var result;
localforage.getItem(key, function(value) {
result = value;
});
return result;
}
I want getValue() to return a value without changing any other code
According to this link
localForage has a dual API that allows you to either use Node-style
callbacks or Promises. If you are unsure which one is right for you,
it's recommended to use Promises.
So you can use any of them if you like. if using promises you can use async/await to wait for the result
localforage.setItem('testKey', 'testValue', async function() {
var value = await getValue('testKey')
console.log(value); // here I get undefined, but I want to get a value
});
async function getValue(key) {
var result = await localforage.getItem(key);
return result;
}
jsfiddle
localforage.setItem('testKey', 'testValue', async function() {//declare function as async
var value = await getValue('testKey'); //wait for the value
console.log(value); // "testValue" value should show in console
});
//declare function as async
async function getValue(key) {
var result = await localforage.getItem(key); //wait for the localforage item
return result;
}
JSFiddle here: https://jsfiddle.net/mvdgxorL/
https://localforage.github.io/localForage/#data-api-getitem, use async/await:
try {
const value = await localforage.getItem('somekey');
// This code runs once the value has been loaded
// from the offline store.
console.log(value);
} catch (err) {
// This code runs if there were any errors.
console.log(err);
}
This code calls a function (getTable()) that returns a promise:
function getTables() {
while (mLobby.tblCount() < 4) {
getTable().then(function(response) {
mLobby.addTable(response);
}, function (error) {
console.error("getTable() finished with an error: " + error);
});
}
}
It never resolves (and eventually crashes due to full memory) because of the clash of the async function call and the normal flow of the while loop. I tried changing the while to an if with a recursive call, but that gave the same result:
function getTables() {
if (mLobby.tblCount() < 4) {
getTable().then(function(response) {
mLobby.addTable(response);
getTables();
}
});
}
In my experience, using Promises inside of a synchronous action like while won't work like you want.
What I've done is use async await to accomplish the same task. Something like...
async function getTables() {
while (mLobby.tblCount() < 4) {
await getTable();
// whatever other code you need...
}
}
So, the while loop will continue to work as expected only after each getTable() call is resolved. Definitely test this code, obviously.
Here's a really simple working example of what I'm talking about: https://codepen.io/alexmacarthur/pen/RLwWNo?editors=1011
Is there a particular reason to do this in a while loop and adding the results to the lobby object while doing it?
Maybe you could use just a standard-for-loop that calls getTable 4 times:
function getTables(limit=4){
let results = [];
for(let i=0; i<limit;i++){
results.push(getTable());
}
return Promise.all(results);
}
Your method will return a promise that will resolve with the array of the result of the given getTable-calls
getTables().then(tables => {
tables.forEach(table => {
if(myLobby.tableCount() < 4) myLobby.addTable(table)
})
}).catch(console.warn)
I'm probably missing the point somewhere here so I'm looking for advice.
I have a nodejs server which is listening for client connections and, based on the data received, makes calls to an API.
The very first call to that API gets an ID which needs to be used on subsequent calls to group them together.
Where I'm struggling is that the call to the API is necessarily asynchronous and in the callback I'm assigning the ID to a variable. While that async call is being processed by the API server, more data is coming in from the client and needs more API calls made BUT I can't fire them until I know the results from the first call as the second calls depend on it.
What's the proper way to handle this? I feel like I should be using Q to promise the results of the first API call to the second, but I'm not sure how it should be structured. Or should I just be queueing up the API calls until the first completes? How would I do that?
Example problem code :
var server = net.createServer();
//set up the callback handler
server.on('connection', handleConnection);
handleConnection(conn) {
//do some stuff...
firstAPICall();
conn.on('data', handleData);
}
handleData(data) {
//do some stuff...
otherAPIcall();
}
firstAPICall() {
client.get("http://myAPI/getID", function (data, response) {
conn.myID = data[0].myID;
}
}
}
otherAPICall() {
//How do I make sure I actually have a value
//in conn.myID from the first function???
client.post("http://myAPI/storeData", { data: {myID:conn.myID, data:someData} }, function (data, response) {
//do some stuff...
}
}
}
Yes, you should be using promises for this. Make a promise for the id that is asynchronously resolved from the first call, and then use it in the subsequent calls:
handleConnection(conn) {
//do some stuff...
var idPromise = firstAPICall();
conn.on('data', function handleData(data) {
//do some stuff...
otherAPIcall(idPromise).then(function(result) {
…
});
});
}
firstAPICall() {
return Q.Promise(function(resolve, reject) {
client.get("http://myAPI/getID", function (data, response) {
resolve(data[0].myID);
});
});
}
otherAPICall(idPromise) {
return idPromise.then(function(myID) {
return new Promise(function(resolve, reject) {
client.post("http://myAPI/storeData", {
data: {myID:myID, data:someData}
}, function (data, response) {
//do some stuff...
resolve(…);
});
});
});
}
Probably you should factor out creating a promise for the result of a client.get call in an extra function. Also make sure to handle errors correctly there and call reject with them. If client would use the node callback conventions, Q even has some nice helper functions for that.
Try using promises. Then use 'then' to call the otherAPICall()
I think you can assume they will be sending data immediately after connecting. So you can simplify and just check in otherAPICall if you have an ID, if not, you can just use a callback. Promises or the async/await keywords might make things sort of nicer down the line but aren't required for this.
var server = net.createServer();
//set up the callback handler
server.on('connection', handleConnection);
handleConnection(conn) {
conn.on('data', handleData(connm, data));
}
handleData(conn, data) {
//do some stuff...
otherAPIcall(conn);
}
checkID(conn, cb) {
if (!conn.myID) {
client.get("http://myAPI/getID", function (data, response) {
conn.myID = data[0].myID;
cb();
});
} else {
cb();
}
}
otherAPICall(conn) {
checkID(conn, function() {
client.post("http://myAPI/storeData", { data: {myID:conn.myID, data:someData} }, function (data, response) {
//do some stuff...
});
});
}
promises can chain values and are always resolved after the callback occurs with the returned value,
function async(value) {
var deferred = $q.defer();
var asyncCalculation = value / 2;
deferred.resolve(asyncCalculation);
return deferred.promise;
}
var promise = async(8)
.then(function(x) {
return x+1;
})
.then(function(x) {
return x*2;
})
.then(function(x) {
return x-1;
});
promise.then(function(x) {
console.log(x);
});
This value passes through all the success callbacks and so the value 9 is logged ((8 / 2 + 1) * 2 - 1).