Asynchronous variables in for loop [duplicate] - javascript

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
I'm working on adding notifications to my chrome extension that lets users chat with one another. Each user has a chats section that lists all the chatrooms they're in, along with the last message they saw. To check if a message was sent while they were away, I'm trying to loop through the chatrooms they're in and seeing if the last message sent was added to the database after the last message they saw was added.
Here's my code, I'm using a noSQL database called firebase:
var checkNotifications = function(user){
var notification = false;
firebase.database().ref('users/'+user+'/chats').once('value', function(snapshot){
for (var chat in snapshot.val()){
var lastMessage = snapshot.val()[chat];
firebase.database().ref('chats/'+chat+'/msgs').once('value', function(snap){
if (Object.keys(snap.val())[0] > lastMessage){
notification = true; // Message is newer than their last seen message
}
});
}
});
}
I'm running into the issue where before the second database call to return the messages from the chatroom currently being looked at in the for loop the for loop already moves on and updates the variable lastMessage, so that variable ends up holding the last message from a chatroom further down the chain.
Is there a way to make this less asynchronous so that the variable lastMessage has the same value for each step in the for loop?

To check if a message was sent while they were away, I'm trying to
loop through the chatrooms they're in and seeing if the last message
sent was added to the database after the last message they saw was
added.
transactions take a function and are the proper tool for this.
When working with data that could be corrupted by concurrent
modifications, such as incremental counters, you can use a transaction
operation. You can give this operation an update function and an
optional completion callback. The update function takes the current
state of the data as an argument and returns the new desired state you
would like to write. If another client writes to the location before
your new value is successfully written, your update function is called
again with the new current value, and the write is retried.
For instance, in the example social blogging app, you could allow users to star and unstar posts and keep track of how many stars a post has received as follows:
function toggleStar(postRef, uid) {
postRef.transaction(function(post) {
if (post) {
if (post.stars && post.stars[uid]) {
post.starCount--;
post.stars[uid] = null;
} else {
post.starCount++;
if (!post.stars) {
post.stars = {};
}
post.stars[uid] = true;
}
}
return post;
});
}

This should work. It captures the variable in a closure: (function(){ ... })();
var checkNotifications = function(user){
var notification = false;
firebase.database().ref('users/'+user+'/chats').once('value', function(snapshot){
for (var chat in snapshot.val()){
(function(){
var lastMessage = snapshot.val()[chat];
firebase.database().ref('chats/'+chat+'/msgs').once('value', function(snap){
if (Object.keys(snap.val())[0] > lastMessage){
notification = true; // Message is newer than their last seen message
}
});
})();
}
});
}
Or this:
var checkNotifications = function(user){
var notification = false;
firebase.database().ref('users/'+user+'/chats').once('value', function(snapshot){
var func = function(lastMessage) {
firebase.database().ref('chats/'+chat+'/msgs').once('value', function(snap){
if (Object.keys(snap.val())[0] > lastMessage){
notification = true; // Message is newer than their last seen message
}
});
};
for (var chat in snapshot.val()){
func(snapshot.val()[chat]);
}
});
}

Related

Twilio Client CallSid Getting Changed in JavaScript

I've implemented a call center in Salesforce with Twilio client JavaScript SDK. I'm trying to save the call record information in Salesforce and I'm using the connection.parameters.CallSid to identify the correct record when the recording call back fires. However my CallSid in client side is getting changed automatically sometimes to a different format and hence the recording call back function can't find the Salesforce end record to update it with the RecordingUrl. Have anyone experienced this before or appreciate any guidance.
Below is my JavaScript code. To be more specific, in the startCall function console.log print the CallSid correctly but when goes to saveLog function it's having a different value with a different format and hence the saved record having an incorrect value.
<script type="text/javascript">
Twilio.Device.setup("{! token }");
var callerSid; // hold the Twilio generated CallSid unique to this call
var parentId; // hold the parent record being called in order to associate as the parent of task being logged for the call
var newTaskId; // hold the id of newly created task
// function to fire when click 2 dial executes
var startCall = function (payload) {
sforce.opencti.setSoftphonePanelVisibility({visible: true}); //pop up CTI softphone
parentId = payload.recordId; // the record that the phone number being called belongs to
var cleanednumber = cleanFormatting(payload.number);
params = {"PhoneNumber": cleanednumber};
var connection = Twilio.Device.connect(params);
callerSid = connection.parameters; // track the unique Twilio CallSid
console.log('clk2dial : ', callerSid.CallSid); **// here it logs correcly as CAc57d05994cd69498e0353a5f4b07f2dc**
setTimeout(function(){
saveLog(); // save the call information in a Task record
}, 2000
);
};
//OpenCTI!!
sforce.opencti.enableClickToDial();
sforce.opencti.onClickToDial({listener : startCall}); // click 2 dial
function cleanFormatting(number) {
//changes a SFDC formatted US number, which would be 415-555-1212 into a twilio understanble number 4155551212
return number.replace(' ','').replace('-','').replace('(','').replace(')','').replace('+','');
}
// save the call information in a Task record
function saveLog() {
var keyPrefix;
var taskToSave;
console.log('callerSid.CallSid : ', callerSid.CallSid); **// surprisingly here it logs as TJSce253eb4-c2a0-47f3-957f-8178e95162aa**
if(parentId != null) {
keyPrefix = parentId.slice(0,3);
}
if(keyPrefix != null && keyPrefix == '003') {
taskToSave = {WhoId:parentId, Type: "Call", CallObject: callerSid.CallSid, entityApiName: "Task", Subject: "Call log"};
} else {
taskToSave = {WhatId:parentId, Type: "Call", CallObject: callerSid.CallSid, entityApiName: "Task", Subject: "Call log"};
}
sforce.opencti.saveLog({value:taskToSave, callback: saveLogCallBack});
}
// call back function for saving the call information in a Task record
var saveLogCallBack = function(response) {
if(response.success) {
newTaskId = response.returnValue.recordId;
console.log('save success! : ', newTaskId);
} else {
console.error('Error saving : ', response.errors);
}
}
</script>
Answering my own question as I got through this. I registered a function for Twilio.Device.connect and in the call back function retrieved the CallSid. Along with that I've updated my click 2 dial function as well accordigly as below. However I was unable to find this approach in Twilio documentation and any comments are welcome.
// function to fire when click 2 dial executes
var startCall = function (payload) {
sforce.opencti.setSoftphonePanelVisibility({visible: true}); //pop up CTI softphone
parentId = payload.recordId; // the record that the phone number being called belongs to
var cleanednumber = cleanFormatting(payload.number);
params = {"PhoneNumber": cleanednumber};
Twilio.Device.connect(params);
};
//OpenCTI!!
sforce.opencti.enableClickToDial();
sforce.opencti.onClickToDial({listener : startCall}); // click 2 dial
// registered a function for Twilio Device connect
Twilio.Device.connect(function(response) {
callSid = response.parameters; // track the unique Twilio CallSid
// nothing change in save function so not posting again
saveLog(); // save the call information in a Task record
});

Firebase won't add full entirety of object when using set() [duplicate]

This question already has answers here:
Create an empty child record in Firebase
(2 answers)
Closed 6 years ago.
I'm working on a chrome extension and pushing data from a Google-authenticated user to Firebase.
I'm listening for a message coming from another JS file and when it comes in, I want to take the "user profile" object (request) and tack on a property to it, called "visitedLinks". visitedLinks should be set to an empty object.
I do 3 console.logs throughout the code, and in all three cases, the console.logs show "visitedLinks" set to an empty object, yet when I push to Firebase, "visitedLinks" isn't a property.
//Relevant 3 console statements are the following
//console.log('request.accountData = ', request.accountData)
//console.log('userObject test #1 = ', userObject)
//console.log('userObject = ', userObject)
var rootRef = new Firebase("https://search-feed-35574.firebaseio.com/");
if (localStorage.userIsAuthenticated) {
console.log('user is authenticaled')
chrome.runtime.onMessage.addListener(
//listen for messages
function(request, sender, sendResponse) {
//url is coming in from a content script, use localStorage.uid to make database call
if (request.url) {
console.log('message coming from content script')
var uid = localStorage.uid;
var url = request.url;
var userRef = rootRef.child(uid);
newLinkRef = userRef.push(url);
//otherwise, we're getting a message from popup.js, meaning they clicked it again, or they've signed in for the first time
} else {
console.log('message coming from popup')
//here we're passing in all the data from the person's Google user account
var googleUID = request.accountData.uid
//then we make a new object with the key as their google UID and the value all the account data
request.accountData.visitedLinks = {}
console.log('request.accountData = ', request.accountData)
var userObject = {};
userObject[googleUID] = request.accountData;
console.log('userObject test #1 = ', userObject)
//here were checking to see if the UID is already a key in the database
//basically, if they've ever logged in
rootRef.once('value', function(snapshot) {
if (snapshot.hasChild(googleUID)) {
//user has authenticated before, they just happened to click the popup again
console.log('already authenticated, you just clicked the popup again')
} else {
console.log('users first DB entry');
//if they're not in the database yet, we need to push userObject to the DB
//and push their current url to the publicLinks array
rootRef.set(userObject, function(error) {
console.log('error = ', error);
console.log('userObject after DB insert = ', userObject)
});
}
})
}
//figure out if this user has entries in the DB already
//just push the link information onto the "links" node of the db object
//if not, push a ref (to the right place)
// console.log(sender)
});
} else {
console.log('user isnt authenticated')
}
So it turns out that you can't insert empty objects into the database. Similar question answered here: Create an empty child record in Firebase

Firebase data gets overwritten ! sought guidance

I am building an Angular App with Firebase.
My intention is to create an object (say Rooms) at the root with 3 child objects (say Room1, Room2 & Room3) . Also, I am trying to create a logic that would check if the Rooms object is there - it wont create it again.
My code was :
var ref = new Firebase(firebaseURL);
ref.child('Rooms').once('value', function (snapshot){
if(snapshot.numChildren() == 0){
// Create Room within a loop
ref.child('Rooms').child(i).set(roomObj);
}else if(snapshot.numChildren() > 0){
// do not create
}
}
But when the code runs - it always enters into the if block !! And creates the child Rooms.
What is my mistake in the code ??
Most likely the value event will be triggered again with the value you expect.
Your solution is to run the code in a transaction.
var ref = new Firebase(firebaseURL);
ref.child('Rooms').transaction(function (data){
if(!data){
var rooms = {};
for (var roomNum=0; roomNum < 3; roomNum++) {
rooms['room'+roomNum] = { name: 'Room '+roomNum };
}
return rooms
}
}
So if the rooms don't exist yet, the above code creates them. If they already exist, the code does nothing (not returning a value, leaves the data unmodified).
Be sure to read the Firebase documentation for transaction.

For loop that calls a function with a promise works just once

I am trying to get the timbre.js recording function to create multiple buffers and store them in an object. The function that creates the recording and stores it in the 'songs' object is here:
var generateSong = function(songtitle){
T.rec(function(output) {
//... composition here, full at: http://mohayonao.github.io/timbre.js/RecordingMode.html
output.send(synth);
}).then(function(buffer) {
var songobject = {}
var song = T("buffer", {buffer:buffer});
songobject['song'] = song;
songobject['buffer'] = buffer.buffer[0];
songobject['samplerate'] = buffer.samplerate;
songs[songtitle] = songobject;
console.log('test message');
});
};
Then I try and call this multiple times with a for loop like this:
for(var i=0;i<5;i++){
var songtitle = generateSongTitle();
var songobject = generateSong(songtitle);
}
However I am only getting one test message logged to the console, so the loop seems to be running once then stopping. I've tried moving the 'then()' onwards part of the code to inside the for loop itself (ie generateSongTitle().then(...) ), but that had the same problem. How can I get the generateSong() function to run multiple times?
Thank you
So, I guess there's actually two possible problems there.
You are not returning the result of output.send
T.rec(function(output) {
//... composition here, full at: http://mohayonao.github.io/timbre.js/RecordingMode.html
return output.send(synth);
}
This will enable the next handler on the promise chain to actually get the buffer value (if it indeed is returned synchronously by send).
Can T.rec run in parallel?
Do you want to record 5 songs at the same time?
You should probably wait for one song to complete before recording the next.
function generate() {
var songtitle = generateSongTitle();
var songobject = generateSong(songtitle);
return songobject;
}
var nextSong = $.when(); // or any empty promise wrapper
for(var i=0;i<5;i++){
nextSong = nextSong.then(generate);
}
This will generate 5 songs sequentially... in theory! Do tell me if it works :)

Dealing with titanium Push notifications ios

Here is my code:
Titanium.Network.registerForPushNotifications({
types: [
Titanium.Network.NOTIFICATION_TYPE_BADGE,
Titanium.Network.NOTIFICATION_TYPE_ALERT
],
success:function(e)
{
var deviceToken = e.deviceToken;
Ti.API.info("Push notification device token is: "+deviceToken);
alert('device token is' +e.deviceToken);
Ti.API.info("Push notification types: "+Titanium.Network.remoteNotificationTypes);
Ti.API.info("Push notification enabled: "+Titanium.Network.remoteNotificationsEnabled);
},
error:function(e)
{
Ti.API.info("Error during registration: "+e.error);
},
callback:function(e)
{
// called when a push notification is received.
//Titanium.Media.vibrate();
var data = JSON.parse(e.data);
var badge = data.badge;
if(badge > 0){
Titanium.UI.iPhone.appBadge = badge;
}
var message = data.message;
if(message != ''){
var my_alert = Ti.UI.createAlertDialog({title:'', message:message});
my_alert.show();
}
}
});
}
The callback function:
callback:function(e)
{
// called when a push notification is received.
//Titanium.Media.vibrate();
var data = JSON.parse(e.data);
var badge = data.badge;
if(badge > 0){
Titanium.UI.iPhone.appBadge = badge;
}
var message = data.message;
if(message != ''){
var my_alert = Ti.UI.createAlertDialog({title:'', message:message});
my_alert.show();
}
}
});
is fired when the push notification is recieved when the app is running in the foreground.
Question , if I have 2 files:
app.js -> newwindow.js
and say that I am in newwindow.js , will I still receive push notifications? (if the code above is all pasted in app.js?)
2) When I recieve a push notification when the app is running in the background, how can I write a callback method for it, so I can tell the app what to do with that notification
3) What is the best way of handling different notifications, i.e. say I need to open different windows, when its in background mode?
notification 1 - > win1.js
notification 2 - > win2.js
notification 3 - > win3.js
First of all you should Implement it in your app(It seems you just copied from docs) and see how actually push notification appear it will make every thing a lot clear.
The callback you have written above will fetch the message that you have send using the Push notification.
As for part 2 of your question : Once the user clicks on the push received your app will come into foreground and the callback will be automatically called.
And to handle different notifications, there are never different push notifications, there is always a single push notification.you have to parse the data and have act according to the condition. you can modify your push message and open window according to it.

Categories

Resources