This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 1 year ago.
Though it is not possible to access a variable inside try block outside its scope, is there any other way to set it up so that it is could be accessible globally? I am using this is node.js.
var edata = 0;
if (file !== null)
try {
new ExifImage({ image: myfile }, function myexif(error, exifData) {
if (error) console.log('Error: ' + error.message);
else {
edata = exifData;
// console.log(edata); data is printed here as wanted.
}
});
} catch (error) {
console.log('Error: ' + error.message);
}
console.log(edata); //need to access data here (outside of the scope)
You have declared it properly https://jsfiddle.net/3u2gwfhz/
var edata = 0;
try {
edata = 2;
} catch (error) {
console.log('Error: ' + error.message);
}
try {
console.log(edata);
} catch (error) {
console.log('Error: ' + error.message);
}
But not sure about your code here, it doesn't look correct:
if (error) console.log('Error: ' + error.message);
else {
edata = exifData;
// console.log(edata); data is printed here as wanted.
return edata;
}
Related
When I am posting a picture from my electron app to blob storage, sometimes it works, and other times I get this error on my terminal:
When I was first working on this app, this problem never showed up, until a week ago. It occurred without making any changes to this part of the app. Any idea on what could cause it.
The electron app goes white, and the dev tools are disconnected.
Here is the code:
var azure = require('azure-storage');
var blobSvc = azure.createBlobService('*connection keys inside here*');
function createBlob() {
blobSvc.createContainerIfNotExists('photos', {publicAccessLevel : 'blob'}, function(error, result, response){
if(!error){
console.log(response);
}
});
console.log("creating image for student#: " + stud_id);
blobSvc.createBlockBlobFromStream('photos', stud_id + '.jpg', toStream(imgData), imgData.size, function(error, result, response){
if(!error){
console.log("file upload: \n" + JSON.stringify(result) + " \n" + JSON.stringify(response));
createPerson();
}
else if (error) {
console.log("error: " + JSON.stringify(error));
}
});
}
In your code, you actually call the createBlockBlobFromStream immediately, probably without container having created. This may cause the problem.
So, you would need to put them within the callback of the createContainerIfNotExists function:
blobSvc.createContainerIfNotExists('photos', {publicAccessLevel : 'blob'}, function(error, result, response) {
if(!error) {
console.log(response);
console.log("creating image for student#: " + stud_id);
blobSvc.createBlockBlobFromStream('photos', stud_id + '.jpg', toStream(imgData), imgData.size, function(error, result, response) {
if(!error) {
console.log("file upload: \n" + JSON.stringify(result) + " \n" + JSON.stringify(response));
createPerson();
} else {
console.log("error: " + JSON.stringify(error));
}
});
}
});
Have a NodeJS process that reaches out to a webservice for something called Kudos. These kudos are sent from one person to another person/or group of people. What I'm trying to do is create one message that has the following:
Kudos from {poster} to {receiver/s}
{Kudos Message}
Currently I have the process working correctly for poster to one receiver. I am struggling with making it work with getting the multiple names of the receivers.
The problem stems from the fact that the section where it returns the users receiving the kudos, it only provides the user id. So I need to make another call to obtain the user's name. I can easily get the promises to work for the one user, but I can seem to get the multiple user properly.
The JSON data that contains the multiple users looks something like this:
"notes_user": [
{
"id": "1060",
"note_id": "795",
"user_id": "411"
},
{
"id": "1061",
"note_id": "795",
"user_id": "250"
},
{
"id": "1062",
"note_id": "795",
"user_id": "321"
}
],
Here is the function that does the majority of the work:
getMaxId returns a database index of that highest kudos currently processed, and getKudos just returns the json dataset of "kudos".
function promisifiedKudos() {
var maxid;
var newmaxid;
Promise.all([getMaxId(), getKudos()])
.then(function(results) {
maxid = results[0];
var kudos = results[1];
newmaxid = kudos[0].id;
return kudos.filter(function(kudo) {
if (maxid < kudo.id) {
return kudo;
}
})
})
.each(function(kudo) {
return getTribeUserName(kudo);
})
.then(function(results) {
return results.map(function(kudo) {
var message = "Kudos from " + kudo.poster.full_name + " to " + kudo.kudo_receiver_full_name + "\r\n";
message += "\r\n";
return message += entities.decode(striptags(kudo.note));
})
})
.each(function(message) {
return postStatus(message);
})
.then(function() {
var tribehr = db.get('tribehr');
console.log(new Date().toString() + ":Max ID:" + newmaxid);
tribehr.update({ endpoint: "kudos" }, { $set: { id: newmaxid } });
})
.done(function(errors) {
console.log("Run Complete!");
return "Done";
});
}
The helper function getTribeUserName()
function getTribeUserName(kudo) {
return new Promise(function(fulfill, reject) {
var id = kudo.notes_user[0].user_id;
var options = {
url: "https://APIURL.com/users/" + id + ".json",
method: "GET",
headers: {
"Authorization": "Basic " + new Buffer("AUTHCODE" + AUTHKEY).toString('base64')
}
}
request.getAsync(options).then(function(response) {
if (response) {
var data = JSON.parse(response.body)
kudo.kudo_receiver_full_name = data.User.full_name;
fulfill(kudo);
} else {
reject("Get Tribe User Name Failed");
}
});
});
}
I've tried adding a helper function that calls the getTribeUserName() that looks like this:
function getTribeUsers(kudo) {
return new Promise(function(fulfill, reject) {
kudo.notes_user.map(function(user) {
//Make calls to a getTribeUserName
})
});
}
But the outcome is that the user names are undefined when the finalized message is put together.
Any pointers in how to use promises better would be extremely helpful. This is really my first stab with them and I hope I'm heading in the right direction. I know I need to add the error checking in, but currently I'm just trying to get the process working for multiple users.
if you need to use the result of a promise passed as parameter of the resolve function then you can catch it in the then onFulfilled callback.
If you need to pass some data obtained within a then method of a chain to another then you just have to return it and catch it through the onFulfilled callback of the following then.
object.somePromise().then(function(param){
var data = someFunction();
return data;
}).then(function(param){
//param holds the value of data returned by the previous then
console.log(param);
});
If it's a matter of getting multiple TribeUserNames asynchronously, then you need somehow to aggregate promises returned by multiple calls to getTribeUserNames().
You could write Promise.all(array.map(mapper)) but Bluebird provides the more convenient Promise.map(array, mapper).
Bluebird's .spread() is also convenient, for referencing maxid and kudos.
Here it is in as simple a form as I can manage :
function promisifiedKudos() {
return Promise.all([getMaxId(), getKudos()])
.spread(function(maxid, kudos) {
var newmaxid = kudos[0].id;
// The following line filters (synchronously), adds TribeUserNames (asynchronously), and delivers an array of processed kudos to the next .then().
return Promise.map(kudos.filter((kudo) => kudo.id > maxid), getTribeUserName)
.then(function(filteredKudosWithTribeUserNames) { // in practice, shorten arg name to eg `kudos_`
return Promise.map(filteredKudosWithTribeUserNames, function(kudo) {
return postStatus("Kudos from " + kudo.poster.full_name + " to " + kudo.kudo_receiver_full_name + "\r\n\r\n" + entities.decode(striptags(kudo.note)));
});
})
.then(function() {
var tribehr = db.get('tribehr');
console.log(new Date().toString() + ":Max ID:" + newmaxid);
return tribehr.update({ endpoint: 'kudos' }, { $set: { 'id': newmaxid } });
});
})
.then(function() {
console.log('Run Complete!');
}).catch(function(error) {
console.log(error);
throw error;
});
}
getTribeUserName() needs to return a promise, and can be written as follows :
function getTribeUserName(kudo) {
var options = {
'url': 'https://APIURL.com/users/' + kudo.notes_user[0].user_id + '.json',
'method': 'GET',
'headers': {
'Authorization': 'Basic ' + new Buffer('AUTHCODE' + AUTHKEY).toString('base64')
}
}
return request.getAsync(options).then(function(response) {
// ^^^^^^
if(response) {
kudo.kudo_receiver_full_name = JSON.parse(response.body).User.full_name;
} else {
throw new Error(); // to be caught immediately below.
}
return kudo;
}).catch(function(error) { // error resistance
kudo.kudo_receiver_full_name = 'unknown';
return kudo;
});
}
Further notes:
By nesting Promise.map(...).then(...).then(...) in the .spread() callback, newmaxid remains available through closure, avoiding the need for an ugly outer var.
Promise.map() is used a second time on the assumption that postStatus() is asynchronous. If that's not so, the code will still work, though it could be written slightly differently.
I just want to do a simple loop in my "alerts" objects, which contains an url, and a word.
For each alert, I do a httpRequest to check if the word is present in the response html code. I yes, I put the status to true.
I also want to update each time the "updatedTo" column, even if I don't find the word in the response html code, but I don't know why...
I wrote this cloud code, but it don't works, or it works sometimes only if I have only items with the word present.
Parse.Cloud.job("updateStatus", function(request, status) {
Parse.Cloud.useMasterKey();
var counter = 0;
var AlertItem = Parse.Object.extend("Alert");
var query = new Parse.Query(AlertItem);
query.each(function(alert) {
var alertTitle = alert.get("title");
var alertUrl = alert.get("url");
var alertStatus = alert.get("status");
var alertWords = alert.get("research");
console.log("Alert : " + alertTitle + " - Check if : " + alertWords + " is on : " + alertUrl)
promise = promise.then(function() {
return Parse.Cloud.httpRequest({
url: alertUrl,
headers: {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/600.1.25 (KHTML, like Gecko) Version/8.0 Safari/600.1.25'
},
}).then(function(httpResponse) {
console.log("We succeded to access to the website");
var htmlCode = httpResponse.text;
if (htmlCode.indexOf(alertWords) >= 0) {
if (alertStatus == false) {
alert.set("status", true);
console.log("new status:true");
return alert.save();
}
} else {
alert.set("status", false);
console.log("new status:false");
//I do this to updated the "updatedTo" field, but it doesn't work
return alert.save();
}
// You need to return a Promise here if non of the above condition meet.
},
function(error) {
console.error('Request failed with response code ' + httpResponse.headers.Location);
// You need to return a rejected promise here.
}
});
});
return promise;
}).then(function() {
status.success('Status updated');
// Set the job's success status
}, function(error) {
// Set the job's error status
status.error("Uh oh, something went wrong.");
});
});
The query.each(callback, options) from documentation.
Iterates over each result of a query, calling a callback for each one. If the callback returns a promise, the iteration will not continue until that promise has been fulfilled. If the callback returns a rejected promise, then iteration will stop with that error. The items are processed in an unspecified order. The query may not have any sort order, and may not use limit or skip.
Parse.Cloud.job("updateStatus", function(request, status) {
Parse.Cloud.useMasterKey();
var counter = 0;
var AlertItem = Parse.Object.extend("Alert");
var query = new Parse.Query(AlertItem);
query.each(function(alert) {
var alertTitle = alert.get("title");
var alertUrl = alert.get("url");
var alertStatus = alert.get("status");
var alertWords = alert.get("research");
console.log("Alert : " + alertTitle + " - Check if : " + alertWords + " is on : " + alertUrl)
return Parse.Cloud.httpRequest({
url: alertUrl,
headers: {
'user-agent': 'A user classic agent'
},
success: function(httpResponse) {
console.log("We succeded to access to the website");
var htmlCode = httpResponse.text;
if (htmlCode.indexOf(alertWords) >= 0) {
if (alertStatus == false) {
alert.set("status", true);
console.log("new status:true");
return alert.save();
}
} else {
alert.set("status", false);
console.log("new status:false");
//I do this to updated the "updatedTo" field, but it doesn't work
return alert.save();
}
// You need to return a Promise here if non of the above condition meet.
},
error: function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.headers.Location);
// You need to return a rejected promise here.
}
});
}).then(function() {
status.success('Status updated');
// Set the job's success status
}, function(error) {
// Set the job's error status
status.error("Uh oh, something went wrong.");
});
});
So, with any help it was difficult, but I finish to find another post who was close to what I need, I adapt it, and I success to use it, it works great with Promises :) :
var _ = require('underscore.js')
Parse.Cloud.job("updateStatus", function(request, response) {
var alerts = Parse.Object.extend("Alert");
var query = new Parse.Query(alerts);
query.equalTo("status", false);
query.find().then(function(alerts) {
var promise = Parse.Promise.as();
_.each(alerts, function(alert) {
var alertUrl = alert.get("url");
...
promise = promise.then(function() {
return Parse.Cloud.httpRequest({
url: alertUrl
}).then(function(httpResponse) {
...
},
function(error) {
...
});
});
});
return promise;
}).then(function() {
response.success("All status updated with success !");
},
function (error) {
response.error("Error: " + error.code + " " + error.message);
});
});
I have a custom synchronization process where I queue up, in order, all of my sync records. When my service retrieves more than 1 sync record, it will process them, then update my last sync date for every successful record, or log my error when it fails (without updating the last sync date) and abort the sync process.
I've implemented the $q.all from AngularJS. Here's a subset of the sync loop:
var processes = [];
for (var i in data) {
if (data[i] === null || data[i].TableName == null || data[i].Query == null || data[i].Params == null) {
// Let's throw an error here...
throw new TypeError("ERROR! The data retrieved from the download sync process was of an unexpected type.");
}
var params = data[i].Params;
var paramsMassaged = params.replaceAll("[", "").replaceAll("]", "").replaceAll(", ", ",").replaceAll("'", "");
var paramsArray = paramsMassaged.split(",");
mlog.Log("Query: " + data[i].Query);
mlog.Log("Params: " + paramsArray);
if (data[i].TableName === "table1") {
var process = $table1_DBContext.ExecuteSyncItem(data[i].Query, paramsArray);
process.then(
function () {
$DBConfigurations_DBContext.UpdateLastSyncDate(data[i].CreatedDate, function (response) {
mlog.Log(response);
});
},
function (response) {
mlog.LogSync("Error syncing record: " + response, "ERROR", data[i].Id);
},
null
);
processes.push(process);
} else if (data[i].TableName === "table2") {
var process = $table2_DBContext.ExecuteSyncItem(data[i].Query, paramsArray);
process.then(
function () {
$DBConfigurations_DBContext.UpdateLastSyncDate(data[i].CreatedDate, function (response) {
mlog.Log(response);
});
},
function (response) {
mlog.LogSync("Error syncing record: " + response, "ERROR", data[i].Id);
},
null
);
processes.push(process);
} else {
mlog.LogSync("WARNING! This table is not included in the sync process. You have an outdated version of the application. Table: " + data[i].TableName);
}
}
$q.all(processes)
.then(function (result) {
mlog.LogSync("---Finished syncing all records");
}, function (response) {
mlog.LogSync("Sync Failure - " + response, "ERROR");
});
Example ExecuteSyncItem function:
ExecuteSyncItem: function (script, params) {
window.logger.logIt("In the table1 ExecuteSyncItem function...");
var primaryKey = params[params.length - 1];
var deferred = $q.defer();
$DBService.ExecuteQuery(script, params,
function (insertId, rowsAffected, rows) {
window.logger.logIt("rowsAffected: " + rowsAffected.rowsAffected);
if (rowsAffected.rowsAffected <= 1) {
deferred.resolve();
} else {
deferred.resolve(errorMessage);
}
},
function (tx, error) {
deferred.reject("Failed to sync table1 record with primary key: " + primaryKey + "; Error: " + error.message);
}
);
return deferred.promise;
}
The problem I'm running into is, if there are more than 1 sync records that fail, then this line displays the same value for all records that failed (not sure if it's the first failure record, or the last).
mlog.LogSync("Error syncing record: " + response, "ERROR", data[i].Id);
How do I get it to display the information for the specific record that failed, instead of the same message "x" times?
As mentioned by comradburk wrapping your processes in a closure within a loop is a good solution, but there is an angular way in solving this problem. Instead of using the native for-in loop, you can do it via angular.forEach() and loop through all the data elements.
var processes = [];
angular.forEach(data, function(item) {
if (item === null || item.TableName == null || item.Query == null || item.Params == null) {
// Let's throw an error here...
throw new TypeError("ERROR! The data retrieved from the download sync process was of an unexpected type.");
}
var params = item.Params;
var paramsMassaged = params.replaceAll("[", "").replaceAll("]", "").replaceAll(", ", ",").replaceAll("'", "");
var paramsArray = paramsMassaged.split(",");
mlog.Log("Query: " + item.Query);
mlog.Log("Params: " + paramsArray);
if (item.TableName === "table1") {
var process = $table1_DBContext.ExecuteSyncItem(item.Query, paramsArray);
process.then(
function () {
$DBConfigurations_DBContext.UpdateLastSyncDate(item.CreatedDate, function (response) {
mlog.Log(response);
});
},
function (response) {
mlog.LogSync("Error syncing record: " + response, "ERROR", item.Id);
},
null
);
processes.push(process);
} else if (item.TableName === "table2") {
var process = $table2_DBContext.ExecuteSyncItem(item.Query, paramsArray);
process.then(
function () {
$DBConfigurations_DBContext.UpdateLastSyncDate(item.CreatedDate, function (response) {
mlog.Log(response);
});
},
function (response) {
mlog.LogSync("Error syncing record: " + response, "ERROR", item.Id);
},
null
);
processes.push(process);
} else {
mlog.LogSync("WARNING! This table is not included in the sync process. You have an outdated version of the application. Table: " + item.TableName);
}
});
$q.all(processes)
.then(function (result) {
mlog.LogSync("---Finished syncing all records");
}, function (response) {
mlog.LogSync("Sync Failure - " + response, "ERROR");
});
The problem is due the closure you have on i. When the callback function executes, the value of i will be the last value in the for loop. You need to bind that value i to a separate, unchanging value. The easiest way to do that is with a self invoking function.
for (var i in data) {
(function(item) {
// Put your logic in here and use item instead of i, for example
mlog.LogSync("Error syncing record: " + response, "ERROR", data[item].Id
})(i);
}
Here's a good read for why closures cause this (it's a pretty common problem):
Javascript infamous Loop issue?
I want to check that I am not saving a duplicate entry for the attend status of an event - so on BeforeSave I am checking that the event rsvp has not already been entered - if it has, I want to know if it needs to be updated. If it does, I want to do an update instead of create a new RSVP entry.
This is my code - I can't seem to get it to work, even with the simplist update inside BeforeSave.
Parse.Cloud.beforeSave("Rsvps", function(request, response) {
var eventid = request.object.get("eventid");
var userid = request.object.get("userid");
var rsvp_status = request.object.get("rsvp_status");
var Rsvps = Parse.Object.extend("Rsvps");
var query = new Parse.Query(Rsvps);
query.equalTo("eventid", eventid);
query.equalTo("userid", userid);
query.first({
success: function(object) {
if (object) {
// response.error("An RSVP for this event already exists.");
request.object.id = object.id;
request.object.set('rsvp_status', "attending");
request.object.save();
} else {
response.success();
}
},
error: function(error) {
response.error("Error: " + error.code + " " + error.message);
}
});
});
I've tried so many variation of this without any joy - this my latest attempt.
#CityLogic you shouldn't have to call that second save in #ahoffer's example, because you are in the beforeSave trigger. Just set the resp_status and call response.success().
UPDATED. I added a check not to not update an existing object if the 'attending' value is correct. Give this a try. If there are any you cannot resolve, add the errors as a comment to this answer.
Parse.Cloud.beforeSave("Rsvps", function (request, response) {
var eventid = request.object.get("eventid");
var userid = request.object.get("userid");
var rsvp_status = request.object.get("rsvp_status");
//Do not re-declare the class
//var Rsvps = Parse.Object.extend("Rsvps");
var query = new Parse.Query("Rsvps");
//Check for existing RSVP
query.equalTo("eventid", eventid);
query.equalTo("userid", userid);
query.first().then(function (object) {
if (object && object.get('rsvp_status') != "attending") {
//RSVP exists and needs updating.
// Do not save the object attached to the request.
//Instead, update existing object.
object.set('rsvp_status', "attending");
object.save().then(function () {
response.error('Updated existing RSVP to "attending"');
},
function (error) {
response.error("Error: " + error.code + " " + error.message);
});
} else {
//Continuing and save the new RSVP object because it is not a duplicate.
response.success();
}
},
function (error) {
response.error("Error: " + error.code + " " + error.message);
});
});