I have a script to create contacts in my database from the contents of my Google Sheet. It first verifies the contact doesn't exist in my database, then adds the contact. I have thousands of contacts, so to reduce the number of contacts in the existing contacts cache, I filter my contact list by state.
var leadsCache = [];
function createContact(leads){
var leadState = '';
for(var i=0; i<leads.length; i++){
if(leads[i].state != leadState){
leadState = leads[i].state;
populateLeadsCache(leadState);
}
var existingLead = leadsCache[leads[i].email];
if(existingLead === undefined){
var leadId = createNewLead(leads[i]);
}
}
}
This works as expected, until I get to a lead with a new state. The code hangs here:
leadState = leads[i].state;
I don't get an error message. I can set the var to empty like this: leadState = '', but I cannot set the value to something else.
In stepping through the code, I can see that leads[i].state has a new string value.
Why can't I change the value? What is the best way to accomplish my desired results?
UPDATE
I wish there was a better error reporting system for Apps Script. Turns out I had an issue in populateLeadsCache (continuous loop) but the system appeared stuck on leadState = leads[i].state;.
Anyone know how to improve the error reporting in Apps Script?
I am answering the question so it can be closed, but leaving the question here in case someone else has a similar issue that isn't really the issue.
Apps Script does not have robust error reporting tools and the debugger failed to highlight the true issue in another part of the code.
When getting a timeout message in Apps Script be aware that it may not have anything to do with where the code appears to break.
For me, the next line of code populateLeadsCache had an issue if the selected state had too many contacts, which resulted in a continuous loop.
Related
I have an android application I developed, that allows the sign up of users. I wrote a firebase cloud function that triggers when a User is created, to generate a 5-digit random integer value for the user who just signed up and it stores the generated code in firebase real time database in the following structure.
MainProject
|
|-Codes
|-UniqueUID_1
|-code:72834
|-UniqueUID_2
|-code:23784
The function that I deployed in order to make sure that the code generation is in the backend, is as seen below. There is a value "checker" which is initialised as 0. I use this value to determine when to exit the while loop. Basically I want the function to generate a 5-digit random value, then check the real time database if that generated value exists in all entries under "Codes", then if it does not exist, append it to the Codes under the relevant UID. If it exists, checker remains zero and the loop continues.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
var checker = 0;
exports.createUserCode = functions.auth.user().onCreate(event => {
while (checker == 0){
var newRand = getUserCode(89999,10000);
var userObject = {
uCode : newRand
};
//run a db query to strategically check value generated
return admin.database().ref("Codes/").orderByChild("uCode").equalTo(newRand).once("value",snapshot => {
if (!snapshot.exists()){
checker = 1;
//add uCode into respective uid slot under Codes
console.log(""+newRand+" : "+event.uid);
return admin.database().ref('Codes/' + event.uid).set(userObject);
}else{
checker = 0;
console.log("uCode "+newRand+" exists");
console.log("uCode generation failed for: "+event.uid);
}
});
}
});
function getUserCode(size, add){
return Math.floor(Math.random()*size+add);
}
I tested it and it worked fine. I thought the problem was solved. However, on the 7th to 11th trial, it gave me a Function returned undefined, expected Promise or value error. I tried it again after a while, and it generated the code fine. Some one else tested it and it brought the same error.
How can I fix this issue to ensure it always works? Thanks in advance.
It's really not clear to me what this function is supposed to do, and the top-level while loop doesn't make sense to me. However, I can see there are a few things wrong with what this code is doing.
First of all, it's depending on the global state checker too heavily. This value will not be the consistent for all function invocations, because they all won't be running on the same server instance. Each running server instance will see a different value of checker. Please watch this video series for more information about how Cloud Functions runs code.
Second of all, when checker has a value of 1 when the function starts, the function will do exactly what the error message says - it will return undefined. It should be pretty easy to see how this happens by reading the code.
To fix this, I suggest first coming up with a clear description of what this function is supposed to do when invoked. Also, I would strongly suggest eliminating dependency on global variables, unless you are absolutely certain you understand what you're doing and the effect they have.
I had the same problem a while ago. ESLint won't allow the function to complete because it evaluates whether every part of your code returns a promise.
From what i can see the first part of the if does return something. Try returning a boolean in the else block.
if (!snapshot.exists()){
checker = 1;
//add uCode into respective uid slot under Codes
console.log(""+newRand+" : "+event.uid);
return admin.database().ref('Codes/' + event.uid).set(userObject);
}else{
checker = 0;
console.log("uCode "+newRand+" exists");
console.log("uCode generation failed for: "+event.uid);
return false;
}
Starting with 10.10, Apple started to also support JavaScript for Automation instead of AppleScript but I really have a hard time to understand their JavaScript syntax.
E.g. let's take this easy AppleScript:
tell application "Mail"
set seletedMails to selection
repeat with aMail in seletedMails
set aSubject to subject of aMail
display dialog aSubject
end repeat
end tell
All it does is displaying a dialog with the subject of every selected e-mail in the Mail application. Can't get much simpler than that, can it? Should be easy to do that in JavaScript, right? So here's my code
function main ( ) {
var Mail = Application('com.apple.mail')
Mail.includeStandardAdditions = true
var selectedMails = Mail.selection
for (var aMail of selectedMails) {
Mail.displayDialog(aMail.subject)
}
}
main()
Result:
Error -1700: Can't convert types.
Okay... what did I do wrong? I cannot call selectedMails.length either, same error. selectedMails doesn't seem to be an array at all. Oh, wait, when I do this var selectedMails = Mail.selections (note the plural form!), then I can do selectedMails.length, but it always gives me 0, regardless how many mails I have selected. And when I do this (note, also uses the plural form):
function main ( ) {
var Mail = Application('com.apple.mail')
Mail.includeStandardAdditions = true
var selectedMails = Mail.selections
for (var aMail of selectedMails) {
Mail.displayDialog(aMail.subject)
}
}
main()
It only says:
Error -2700: Script error.
What extremely helpful error messages we get here, not!
You were very close. 😉
Generally, with JXA, you need to use a parenthesis at the end of a command to GET the values. So, you needed:
Mail.selection()
aMail.subject()
Here's the complete script, with my changes:
'use strict';
var app = Application.currentApplication()
app.includeStandardAdditions = true
var Mail = Application('com.apple.mail')
var selectedMails = Mail.selection();
for (var aMail of selectedMails) {
app.displayDialog(aMail.subject());
}
I also added the var app = Application.currentApplication(), as this is considered the best practice by many, and used by Apple in all of their documents.
See Introduction to JavaScript for Automation Release Notes
I am debugging a javascript/html5 web app that uses a lot of memory. Occasionally I get an error message in the console window saying
"uncaught exception: out of memory".
Is there a way for me to gracefully handle this error inside the app?
Ultimately I need to re-write parts of this to prevent this from happening in the first place.
You should calclulate size of your localStorage,
window.localStorage is full
as a solution is to try to add something
var localStorageSpace = function(){
var allStrings = '';
for(var key in window.localStorage){
if(window.localStorage.hasOwnProperty(key)){
allStrings += window.localStorage[key];
}
}
return allStrings ? 3 + ((allStrings.length*16)/(8*1024)) + ' KB' : 'Empty (0 KB)';
};
var storageIsFull = function () {
var size = localStorageSpace(); // old size
// try to add data
var er;
try {
window.localStorage.setItem("test-size", "1");
} catch(er) {}
// check if data added
var isFull = (size === localStorageSpace());
window.localStorage.removeItem("test-size");
return isFull;
}
I also got the same error message recently when working on a project having lots of JS and sending Json, but the solution which I found was to update input type="submit" attribute to input type="button". I know there are limitations of using input type="button"..> and the solution looks weird, but if your application has ajax with JS,Json data, you can give it a try. Thanks.
Faced the same problem in Firefox then later I came to know I was trying to reload a HTML page even before setting up some data into local-storage inside if loop. So you need to take care of that one and also check somewhere ID is repeating or not.
But same thing was working great in Chrome. Maybe Chrome is more Intelligent.
With the following code, I'm able to successfully create a Parse.Object, and view it in the databrowser:
// This code executes correctly:
var UPrefs = Parse.Object.extend("uPrefs");
var uPrefs = new UPrefs();
uPrefs.setACL(new Parse.ACL(Parse.User.current()));
uPrefs.save();
After the promise has been fulfilled, the current user can no longer update the object:
// But then this code throws a 403 error
uPrefs.save();
Here are the error details:
code: 119
"This user is not allowed to perform the update operation on uPrefs. You can
change this setting in the Data Browser."
Even though the data browser shows the ACL for the row set (w/ both read & write privileges) to the user who created it, and is then trying to update it.
Can anyone spot what I'm doing wrong?
Many thanks if you can help!
With thanks to WandMaker's comments for leading me in the right direction:
To solve this I needed to:
- add a user column to the uPrefs Parse.Object model.
- add a pointer to the user column in the class level permissions, and set that to have read/create/write/update/destroy permissions.
Once that's done, the following code works:
var UPrefs = Parse.Object.extend("uPrefs");
var uPrefs = new UPrefs();
uPrefs.set("user", Parse.User.current());
uPrefs.setACL(new Parse.ACL(Parse.User.current()));
uPrefs.save();
And then all future save() calls work as well.
I've been creating a tool, and needed to create a series of dropdown menus to select a profile/view id (from google analytics) that populates the next menu on change of the previous one (selecting the correct properties for the correct accounts, and then the correct profiles for the correct properties).
To this end I made a small jquery/javascript for loop system which I think is actually quite messy but am not sure on how to improve on it (not part of the question though, but this could be one of the reasons I'm having the problem, although I'm not sure).
This script works across all the browsers I've tested, including mobile devices which I was really happy about.
However, when the tool was launched, two people (out of about a hundred) came back saying that the profile/view hadn't been selected. Which was very curious, since I couldn't replicate this error.
I had been in contact with one of the people and tried debugging (albeit a slow process through long series of meetings etc), but couldn't find a fix for it, (although I think I managed to isolate the problem, which will be pointed out after the code sample).
So my question is this. What could be causing this length of undefined error, and why is it only happening for 1-2 people out of a large sum of them (appears to be in jquery.min.js:2 using jquery version 1.11.1?). The error seems to be occuring when the property is changed, which is strange since the profiles are filling out correctly. Also I asked if the client could use different browsers and accounts but the same error kept happening.
Here is the code that creates the dropdowns:
function fillDropdownMenus(){
var accounts = <?php echo json_encode($accountsArray); ?>;
var propertiesSelectHtml = '<div class="col-xs-12 col-md-4 properties"><select class="col-xs-12" id="properties"><option selected="selected">PROPERTY NAMES</option></select></div>';
var profilesSelectHtml = '<div class="col-xs-12 col-md-4 profiles"><select class="col-xs-12" id="profiles"><option selected="selected">VIEW NAMES</option></select></div>';
accounts.forEach(function(account){
var accountIterator = 0;
account.account['id'].forEach(function(accountId){
$('#accounts').append('<option value="'+accountId+'">'+account.account['name'][accountIterator]+'</option>');
accountIterator++;
});
});
$('.accounts').on('change','#accounts', function(event){
var currentAccount = $('#accounts').val();
$('.properties').remove();
$('.profiles').remove();
$('.accounts').after(propertiesSelectHtml);
accounts.forEach(function(account){
$.each(account.account, function(accountkey, accountvalue){
if(accountvalue == currentAccount){
var propertyIterator = 0;
account.account['property']['id'].forEach(function(propertyId){
$('#properties').append('<option value="'+propertyId+'">'+account.account['property']['name'][propertyIterator]+'</option>');
propertyIterator++;
});
}
});
});
$('.properties').on('change','#properties', function(ev){
var currentProperty = $('#properties').val();
$('.profiles').remove();
$('.properties').after(profilesSelectHtml);
accounts.forEach(function(account){
$.each(account.account['property'], function(propertykey, propertyvalues){
if($.type(propertyvalues) == 'object' || $.type(propertyvalues) == 'array'){
for(var k in propertyvalues){
var propertyvalue = propertyvalues[k];
if(propertyvalue == currentProperty){
var profileIterator = 0;
account.account['property']['profile']['id'].forEach(function(profileId){
$('#profiles').append('<option value="'+profileId+'">'+account.account['property']['profile']['name'][profileIterator]+'</option>');
profileIterator++;
});
}
}
} else {
if(propertyvalue == currentProperty){
var profileIterator = 0;
account.account['property']['profile']['id'].forEach(function(profileId){
$('#profiles').append('<option value="'+profileId+'">'+account.account['property']['profile']['name'][profileIterator]+'</option>');
profileIterator++;
});
}
}
});
});
$('#profiles').on('change', function(e){
$('#view_id').removeAttr('value');
var currentProfile = $('#profiles').val();
$('#view_id').val(currentProfile);
});
});
});
}
fillDropdownMenus();
And the object structure:
Object -> account (object) -> id (array)
-> name (array)
-> property (object) -> id (array)
-> name (array)
-> profile (object) -> id (array)
-> name (array)
Thank you for your input on this issue of mine as I've been bashing my head against the wall for a couple of days trying to figure this out!
EDIT: http://codepen.io/zephyr/pen/VYQPKQ Here's a codepen of the list in action.
This appeared to be an error in my javascript being very inconsistent (having so many different for loops was not helping). After I changed them all to the normal .each jquery loop it seemed to fix the problem, and made the whole menu set work much better, as there were some logical errors as well (I wasn't filtering the profiles by property, it was just displaying them all for that account when the property was selected).