WebSocket response as Promise with multiple messages problem - javascript

I am using angularjs for a web site where I have to download data every seccond. To do that I am using a WebSocket, the problem is that I am sending a lot of different requests of different types. To elaborate the request I use Promises but some times the answers get to the wrong function. Let me make an example:
So I have a service that is making the request and gets the result as Promise.
socketService.$inject = [];
function socketService(){
let service = this;
let server = new WebSocket('ws://localhost:8090');
let isOpen = false;
server.onopen = function(){
isOpen = true;
};
server.onerror = function(){
//TODO handle error
};
service.sendRequest = function(action, data){
return new Promise(function (resolve, reject) {
if (isOpen) {
server.send(encodeRequest(action, data));
server.onmessage = function (message) {
resolve(JSON.parse(message.data));
}
}
server.onopen = function () {
server.send(encodeRequest(action, data));
server.onmessage = function(message) {
resolve(JSON.parse(message.data));
isOpen = true;
}
};
server.onerror = function (error) {
reject(error);
}
})
};
}
And I use it like this:
socketService.sendRequest('get_events', {})
.then(function (response) {
//DO SOMETHING WITH THE RESPONSE
return socketService.sendRequest('get_history', {
fromDate: value,
toDate : value,
tag : value,
event : value
})
})
.then(function (response) {
//DO SOMETHING WITH THE RESPONSE
});
The problem is that if I make the requests like this (when one promise is resolved I call the other it works perfectly) but if instead I do for example some requests every second and then in the same time I make a new request (socketService.sendRequest(...)) the results get messed up.
I figured out that the problem is that if I make a request and before I get the result of the first request I make another one, the response of the first request goes to the second one.
I don't understand why this happens because I return every time a new Promise, so it should resolve the response of that Promise instead of resolving the response of the next Promise.
I read that once a promise is resolved it cannot be used again (it returns the last result over and over) but I create a new one every time.
Can someone explain to me what happens and how I could fix this?

You'll need to put a unique ID into the messages so that you can ensure that each response gets correlated to the correct request. But before diving into coding this, you may want to look at json rpc 2.0. This is a standard, with a couple of different javascript implementations, that will handle most of this for you. I have had good luck with https://github.com/jershell/simple-jsonrpc-js
You also asked why your Promise code is not working the way you expect it to. The answer is... well, Promise doesn't work the way you expect it to. :-) OK, let me try to be more helpful. Your Promise code is altering the server's callback methods (onOpen, onMessage, etc.) so that they will call the promise's "resolve" function. The problem is that, even though you have multiple promises, you only have one server. So each Promise you create just points the server callback methods to a new resolve function.
But... skip all that. Someone else has already figured this out for you. Use JSON RPC 2.0.
Duncan

I was also facing similar issues, as WebSocket message work as events. I have written one library where we can write Promise based send and receive the message.
https://github.com/pathikdevani/linka

Related

Dialogflow: Is this being used in an async call

I'm new to the javascript world and have been tinkering with Actions on Google. I had an action that was previously running but after attempting to simplify my code, I've been running into a new error I cannot seem to figure out. The code is supposed to make a call to a database and return information to the user based on the date they have selected.
Problem:
The code is supposed to make a call to a database and return information to the user based on the date they have selected. The intent seems to break down when I call the URL using axios. When I test my function I receive the following error from the Google Cloud Platform: "Error: No response has been set. Is this being used in an async call that was not returned as a promise to the intent handler?"
app.intent('Moon', (conv, {date}) => {
// Sets date to today if none given
if (!date) {
date = new Date().toISOString();
}
// Slices date string to match date format in database
const dateSlice = date.slice(5, 9);
// Call to row in dabase for the given date
function getData() {
return axios.get(`example.com/search?Date=${dateSlice}`);
}
return getData().then(res => {
res.data.map(con => {
conv.ask(`On X date there will be a ${con.Object1}`);
});
});
});
I don't know much about Promise and await but that seems to be the issue. I'm not sure how I was able to get my code to run before without these objects. I've tried to insert a Promise object before my return but it makes the rest of the function unreachable. I also checked to see if Axios had any updates but it has not, I am on the latest version. Does the error have to do with one of the returns perhaps?
It could be related to Promises, but you seem to be handling them correctly.
The call to axios.get() is returning a Promise...
... which you are returning in getData()...
... which is returned in the 'Moon' Intent handler as part of the getData().then() block.
I suspect more that this is a logic problem. If res.data is an empty array, then there will be no calls to conv.ask(), so you end up not asking anything.
There is also a problem if res.data has more than two items. In this case, you'll generate an error because you've replied with more than two "simple" responses.
Either way - you may wish to log res and/or res.data to make sure you're getting back what you think.

How to recover from errors in rxjs?

I'm trying to understand how to consume observable sequences and how to recover from errors.
My example is a bit contrived but my real implementation is a bit too complex to show here. Anyway, I have someObservable that emits values when the user clicks in the UI. This should trigger a request to the API (GET/POST/etc). The problem is that if the API returns an error, postData isn't called anymore after that.
I've make an example here that shows the problem. I've found that I can use Rx.Observable.of instead of Rx.Observable.throw to keep things running. But that will emit a value to the subscribe function. And in my real application postData is a service that is reused by multiple parts of the application and I'm not sure that I want to use of instead of throw there.
So my question is, how do you normally handle things like these?
let someObservable = Rx.Observable.timer(1, 1000);
someObservable
.switchMap(data => {
return postData(data);
})
.catch(error => {
console.log("POST failed");
})
.subscribe(val => console.log("Success: " + val));
function postData(data) {
console.log("Will post " + data);
if (data !== 2) {
return Rx.Observable.of(true);
}
else {
return Rx.Observable.throw(new Error("400 Bad Request or something"));
}
}
http://jsbin.com/naroyeyive/3/edit?js,console
If you want to send the request again, you may want to look into retry and retryWhen instead of catch.
catch will use the returned Observable as new "source". See this bin for an example.
retry(When) will "re-subscribe" whenever an error occurs, based on the configuration you pass to the operators. You can find examples and more information here: https://www.learnrxjs.io/operators/error_handling/retrywhen.html

Backbone collection.create and promises

Im currently implementing a Purchase Order type View. Where I have a PurchaseOrder table and a PurchaseOrderLine table for the items. The first thing I do when the use presses the save button I first save the Purchase Order and then I retrieve the PurchaseOrderID and save to each individual PurchaseOrder item. The problems is the following:
Promise.resolve( app.PurchaseOrder.create(formData) ).then(function(response){
purchaseOrderID = response.collection.models[0].attributes.id;
for(var key in formLineData){
if(formLineData.hasOwnProperty(key)){
formLineData[key]['requestID'] = purchaseOrderID;
app.PurchaseOrderLines.create(formLineData[key]);
}
}
}).catch(function(error){
console.log(error);
})
formData is the PurchaseOrder data, formLineData is the PurchaseOrderLine Data(I do the for loop to insert requestIDs to all items).
I am using a Promise because collection.fetch does not return a promise on Backbone(I think my implementation isn't quite correct because Promise.resolve() is use to make thenables a promise and in this case it isn't). The problem is that when the save button is clicked the then part passes even PurchaseOrder hasn't been created. So when it gets to the PurchaseOrderLine.create, all the items are saved without a PurchaseOrderID. I have two options:
Add a server validation for this. The problem with this is that everytime is going to return an error and this can be bothersome to the user.
Add a setTimeout to at least wait a couple seconds for the write to over on the server.
Could please shed a light on this topic.
you can try something like this
app.PurchaseOrder.create(formData).then(function (response) {
var purchaseOrderID = response.collection.models[0].attributes.id;
return new Promise(async function (resolve) {
for (var key in formLineData) {
if (formLineData.hasOwnProperty(key)) {
formLineData[key]["requestID"] = purchaseOrderID;
await app.PurchaseOrderLines.create(formLineData[key]);
}
}
resolve()
});
});
or maybe doing something like this using Promise.all
Promise.all(formLineData.map(()=>app.PurchaseOrderLines.create(formLineData[key])))

Parse Cloud Code Ending Prematurely?

I'm writing a job that I want to run every hour in the background on Parse. My database has two tables. The first contains a list of Questions, while the second lists all of the user\question agreement pairs (QuestionAgreements). Originally my plan was just to have the client count the QuestionAgreements itself, but I'm finding that this results in a lot of requests that really could be done away with, so I want this background job to run the count, and then update a field directly on Question with it.
Here's my attempt:
Parse.Cloud.job("updateQuestionAgreementCounts", function(request, status) {
Parse.Cloud.useMasterKey();
var query = new Parse.Query("Question");
query.each(function(question) {
var agreementQuery = new Parse.Query("QuestionAgreement");
agreementQuery.equalTo("question", question);
agreementQuery.count({
success: function(count) {
question.set("agreementCount", count);
question.save(null, null);
}
});
}).then(function() {
status.success("Finished updating Question Agreement Counts.");
}, function(error) {
status.error("Failed to update Question Agreement Counts.")
});
});
The problem is, this only seems to be running on a few of the Questions, and then it stops, appearing in the Job Status section of the Parse Dashboard as "succeeded". I suspect the problem is that it's returning prematurely. Here are my questions:
1 - How can I keep this from returning prematurely? (Assuming this is, in fact, my problem.)
2 - What is the best way of debugging cloud code? Since this isn't client side, I don't have any way to set breakpoints or anything, do I?
status.success is called before the asynchronous success calls of count are finished. To prevent this, you can use promises here. Check the docs for Parse.Query.each.
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.
So, you can chain the count promise:
agreementQuery.count().then(function () {
question.set("agreementCount", count);
question.save(null, null);
});
You can also use parallel promises to make it more efficient.
There are no breakpoints in cloud code, that makes Parse really hard to use. Only way is logging your variables with console.log
I was able to utilize promises, as suggested by knshn, to make it so that my code would complete before running success.
Parse.Cloud.job("updateQuestionAgreementCounts", function(request, status) {
Parse.Cloud.useMasterKey();
var promises = []; // Set up a list that will hold the promises being waited on.
var query = new Parse.Query("Question");
query.each(function(question) {
var agreementQuery = new Parse.Query("QuestionAgreement");
agreementQuery.equalTo("question", question);
agreementQuery.equalTo("agreement", 1);
// Make sure that the count finishes running first!
promises.push(agreementQuery.count().then(function(count) {
question.set("agreementCount", count);
// Make sure that the object is actually saved first!
promises.push(question.save(null, null));
}));
}).then(function() {
// Before exiting, make sure all the promises have been fulfilled!
Parse.Promise.when(promises).then(function() {
status.success("Finished updating Question Agreement Counts.");
});
});
});

how to manually slow down asynchronous javascript requests to parse.com in order to stay below 30 requests per second?

I'm fetching a collection called logCollection from parse.com in a node JS script on my machine. It has 200 elements. Each log has a link to another table (a pointer) called start. I need to fetch this one too.
Here is my code
Parse.User.logIn("user", "pass").then(function(user) {
// Do stuff after successful login.
console.log('succesfully logged in');
return logCollection.fetch();
}).then(function(content) {
console.log('done fetching logs: ' + logCollection.length);
var promises = [];
_.each(logCollection.models, function(thisLog) {
promises.push(thisLog.attributes.start.fetch());
});
// Return a new promise that is resolved when all of the deletes are finished.
return Parse.Promise.when(promises);
});
The thing is, it will fire at least 200 (start) fetch per second, and it will cause problems with the 30 requests per second limit at parse.com.
Is there a better way to do this? How can I slow down the way js fires the requests?
thanks
In a Parse Query, you can get the fully-fetched objects which are pointed to by that object, by using the include method on the query:
var query = new Parse.Query("SomeClass");
query.include('columnName');
query.find().then(function(results) {
// each result will have 'columnName' as a fully fetched parse object.
});
This also works with sub-sub objects:
query.include('columnName.nestedColumnName');
or as an array:
query.include(['columnName', 'anotherPointerColumn']);
I came out with this solution that works very good. It was all this time on the parse documentation.
https://www.parse.com/docs/js_guide#promises-series
The following code will fire one request only after the last one has been finished. Doing so, I can fire many requests without worrying about getting to the limit.
var query = new Parse.Query("Comments");
query.equalTo("post", 123);
query.find().then(function(results) {
// Create a trivial resolved promise as a base case.
var promise = Parse.Promise.as();
_.each(results, function(result) {
// For each item, extend the promise with a function to delete it.
promise = promise.then(function() {
// Return a promise that will be resolved when the delete is finished.
return result.destroy();
});
});
return promise;
}).then(function() {
// Every comment was deleted.
});

Categories

Resources