I am using BehivourSubject for recieve data, also using websocket but websocket is not important for now. For now is very important why I always got duplicated message from BehivourSubject.
Check code in service:
hubMessage$ = new BehaviorSubject({});
public startConnection = (id: number) => {
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl('https://api/hub')
.build();
this.hubConnection
.start()
.then(() => {
console.log('connection established')
}
)
.catch(err => {
console.log('Error while starting connection: ' + err)
this.retryConnection();
})
}
public newLocationRecieved() {
this.hubConnection.on('NewLoc', (data) => {
console.log('new location recieved' , data)
this.hubMessage$.next(data);
})
}
public sendDriverId(id: number = 1) {
this.hubConnection.send('SubOnDriver', { driverId: id })
}
Every 20 seconds i got new location. I need to remove previous data from this hubMessage or what ever but my software crash after one minute because duplicated message.
Imagine that your messages are duplicated. To get one first, then two, then four, then sixty..32....64..
Why?
How to solve this ?
I am using this message in component:
Component code:
ngOnChanges() {
this.dispatchDetails;
this.createMarkers();
}
createMarkers() {
console.log('Connection start right now ', this.dispatchDetails)
this.newCoordinate();
}
private newCoordinate = () => {
this.signalRService.hubMessage$.subscribe(
(data: any) => {
console.log('recieved new coordinate ?', data); // HERE I GOT SO MUCH DUPLICATED MESSAGE
this.signalRService.newLocationRecieved()
this.locationCoords = data;
if (this.locationCoords.location) {
this.latitude = this.locationCoords?.location?.latitude
this.longitude = this.locationCoords?.location?.longitude
}
}
)
}
I probably need to clear my variable...
Or maybe is problem with websocket connection ?
is the websocket connection duplicated? Which I don't believe
you probably know that I can't make minimal reproduction code because these are web sockets ...
This is because you are calling createMarkers() function from ngOnChanges life cycle. ngOnChanges will be called again and again when there is component scoped value change. So, new subscriptions will be created again and again, so you have bunch of duplicated messages. You need to call that function only when you create a component or whatever only one time. Or you need to kill existing subscription first before creating a new subscription.
You've subscribed to this.signalRService.hubMessage$ and on any new data you receive from that behavior subject you call this.signalRService.newLocationRecieved()
this.signalRService.hubMessage$.subscribe(
(data: any) => {
...
this.signalRService.newLocationRecieved()
}
which in turn creates a brand new callback
public newLocationRecieved() {
this.hubConnection.on('NewLoc', (data) => {
this.hubMessage$.next(data);
})
}
hence your duplicated values. What I would suggest is moving the callback into the startConnection function
public startConnection = (id: number) => {
...
this.hubConnection.on('NewLoc', (data) => {
console.log('new location recieved', data)
this.hubMessage$.next(data);
});
}
Edit:
Also this.createMarkers(); should be moved out of ngOnChanges into ngOnInit. All credit to #Liu Zhang
In the message handler inside newCoordinate, you call this.signalRService.newLocationRecieved() , which sets up another event handler that publishes another message into the same hubMessage$. You want to do this only once, probably in startConnection.
Related
So I'm trying to make a script that will connect a bot to a kahoot. However I can only use a certain name one time otherwise I will get a duplicate name error, even after closing all sockets, terminating sockets, removing listeners, etc. Is there something I'm missing?
Edit: added the cleanup on process end that still doesn't work
var Kahoot = require("kahoot.js-latest");
var colors = require('colors');
var bot = new Kahoot;
function cleanup() {
bot.leave();
bot.removeAllListeners();
bot.socket.close();
bot.socket.terminate();
}
bot.join(process.argv[2], "a")
.then(() => {
console.log("Joining...".cyan);
})
.catch((err) => {
console.log(err);
cleanup();
});
[`exit`, `SIGINT`, `SIGUSR1`, `SIGUSR2`, `SIGTERM`].forEach((eventType) => {
process.on(eventType, (code) => {
for (n in bots) {
cleanup();
}
console.log("Exiting Process with code: ".yellow + code);
})
})
After terminating the process and trying to run it again with the same name, I get a duplicate name error even though surely that connection should be destroyed by now
Edit: the error:
{
description: 'Duplicate name',
type: 'loginResponse',
error: 'USER_INPUT'
}
I have script to move data from one platform to another. The source db allows only 100 records to be fetched in a single request. So I created a routine to fetch by batches of 100 which works fine I guess.
Now I try to process each records of 100 and do the necessary transformations (which involves axios call to get certain data) and create a record in firebase firestore.
Now when I run this migration in firebase express node, I get socket hang up ECONNRESET.
I know this is caused by wrong handling of promises.
Here is what my code looks like:
import { scrollByBatches } from "../helpers/migrations/apiScroll";
import { createServiceLocation } from "../helpers/locations";
const mapServiceLocationData = async (serviceLocation: any, env: string) => {
try {
const migratedServiceLocation: any = {
isMigrated: true,
id: serviceLocation._id,
};
if (serviceLocation.list?.length) {
await Promise.all(serviceLocation.ids.map(async (id: string) => {
const { data } = await dbEndPoint.priceMultiplier({ id }); // error says socket hangup on this call
let multiplierUnit;
let serviceType;
if (data.response._id) {
multiplierUnit = data.response;
const result = await dbEndPoint.serviceType({ id: multiplierUnit.service_custom_service_type }); // error says socket hangup on this call
if (result.data.response._id) {
serviceType = result.data.response.type_text;
migratedServiceLocation.logs = [...multiplierUnit.history_list_text, ...migratedServiceLocation.logs];
}
}
}));
}
await createServiceLocation(migratedServiceLocation); // create record in destination db
} catch (error) {
console.log("Error serviceLocation: ", serviceLocation._id, JSON.stringify(error));
}
return null; // is this even necessary?
};
export const up = async () => {
try {
// get 100 docs from source db => process it.. => fetch next 100 => so on...
await scrollByBatches(dbEndPoint.serviceLocation, async (serviceLocations: any) => {
await Promise.all(
serviceLocations.map(async (serviceLocation: any) => {
await mapServiceLocationData(serviceLocation);
})
);
}, 100);
} catch (error) {
console.log("Error", JSON.stringify(error));
}
return null; // is this even necessary?
};
The error I get in firebase functions console is:
For clarity on how the fetch by batches looks like:
const iterateInBatches = async (endPoint: any, limit: number, cursor: number, callback: any, resolve: any, reject: any) => {
try {
const result = await endPoint({ limit, cursor });
const { results, remaining }: any = result.data.response;
if (remaining >= 0) {
await callback(results);
}
if ((remaining)) {
setTimeout(() => {
iterateInBatches(endPoint, limit, (cursor + limit), callback, resolve, reject);
}, 1000); // wait a second
} else {
resolve();
}
} catch (err) {
reject(err);
}
};
export const scrollByBatches = async (endPoint: any, callback: any, limit: number, cursor: number = 0) => {
return new Promise((resolve, reject) => {
iterateInBatches(endPoint, limit, cursor, callback, resolve, reject);
});
};
What am I doing wrong? I have added comments in the code sections for readability.
Thanks.
There are two cases when socket hang up gets thrown:
When you are a client
When you, as a client, send a request to a remote server, and receive no timely response. Your socket is ended which throws this error. You should catch this error and decide how to handle it: whether to retry the request, queue it for later, etc.
When you are a server/proxy
When you, as a server, perhaps a proxy server, receive a request from a client, then start acting upon it (or relay the request to the upstream server), and before you have prepared the response, the client decides to cancel/abort the request.
I would suggest a number of possibilities for you to try and test that might help you solve your issue of ECONNRESET :
If you have access to the source database, you could try looking
there for some logs or metrics. Perhaps you are overloading the
service.
Quick and dirty solution for development: Use longjohn, you get long
stack traces that will contain the async operations. Clean and
correct solution: Technically, in node, whenever you emit an 'error'
event and no one listens to it, it will throw the error. To make it
not throw, put a listener on it and handle it yourself. That way you
can log the error with more information.
You can also set NODE_DEBUG=net or use strace. They both provide you
what the node is doing internally.
You could restart your server and run the connection again, maybe
your server crashed or refused the connection most likely blocked by
the User Agent.
You could also try running this code locally, instead of in cloud
functions to see if there is a different result. It's possible that
the RSG/google network is interfering somehow.
You can also have a look at this GitHub issue and stackoverflow
thread to see the common fixes for the ECONNRESET issue and see if
those help resolve the issue.
Suddenly I get an error on a web usb device that connects with my Angular app.
The error reads: An operation that changes interface state is in progress.
Edit: more code:
Selecting device & opening connection:
getDeviceSelector() {
return navigator.usb
.requestDevice(this.options)
.then((selectedDevice) => {
this.device = selectedDevice;
return this.device.open(); // Begin a session.
});
}
Communicating with the device (Raspberry Pi)
Start communication with the web-usb on the Pi:
connectedDevice
.then(() => this.device.selectConfiguration(1)) // Select configuration #1 for the device.
.then(() => this.device.claimInterface(0)) // Request exclusive control over interface #0.
.then(() => {
// Read data every 40 ms
this.interval = interval(40).subscribe(async () => {
await this.read();
});
})
Handle the reading of all the data that is being send:
async read() {
const result = await this.readOneLine();
this.readCallbacks.forEach((callback) => {
callback(result);
});
}
readOneLine() {
return this.device.transferIn(1, 8 * 1024).then(
(result) => {
return new Uint8Array(result.data.buffer);
},
(error) => {
console.error(error);
}
);
}
From there on, we use the readCallbacks function to pass the data we got from to device to a custom event that is been fired.
The error might be related to the new Chrome update, but I can not find changes to the navigator.usb or any other USB related mechanics.
New info will be added as soon as I have it!
In my case, the problem only occurred on some Windows laptops. I had to #1 safely remove the USB-device and #2 plug it in another port. After that, the connection was normal just like before (also on the original USB-port).
It suddenly happened on different computers and at the same time, but we were unable to see the exact environment that caused this issue.
I am trying to do a couple things with an http request used to authorize sign in credentials.
First, I am trying to utilize navigateByUrl('/') to redirect the user back to the home page after a successful login/registration.
Second, I want to pass along errors I receive from the server to a previously established empty list of errors.
submitForm() {
this.isSubmitting = true;
this.errors = new Errors();
let credentials = this.authForm.value;
this.userService.attemptAuth(this.authType, credentials)
.subscribe(
data => this.router.navigateByUrl('/'),
err => {
this.errors = err;
this.isSubmitting = false;
}
);
}
My thought is that I am not supposed to be including both of my arrow functions within the subscribe() method. Should I be piping both of these functions instead? I have a good idea of how to use catchError() within a pipe() call, but am a little unclear on how to perform the url redirect within a pipe().
Any advice would be much appreciated, thanks!
Attempting to answer your original question: If you wanted to navigate on a pipe, you could do something like this:
submitForm() {
this.isSubmitting = true;
this.errors = new Errors();
let credentials = this.authForm.value;
this.userService.attemptAuth(this.authType, credentials)
.pipe(
tap(() => this.router.navigateByUrl('/'))
)
.subscribe(
(data) => {
this.isSubmitting = false;
},
(error) => {
this.errors = error;
this.isSubmitting = false;
}
);
}
I would discourage this because it simply adds more overhead for something you could already do in the subscribe();
We currently have a VueJS application and I am looking at migrating it to Cycle.js (first major project).
I understand in Cycle.JS we have SI and SO for drivers (using adapt()); naturally a WebSocket implementation fits this as it has both read and write effects.
We use Phoenix (Elixir) as our backend using Channels for soft real-time communication. Our client-side WS library is Phoenix herehttps://www.npmjs.com/package/phoenix.
The example on Cycle.js.org is perfect if you know how to connect.
In our case, we authenticate using a REST endpoint which returns a token (JWT) which is used to initialize the WebSocket (token parameter). This token cannot simply be passed into the driver, as the driver is initialized when the Cycle.js application runs.
An example (not actual code) of what we have now (in our VueJS application):
// Code ommited for brevity
socketHandler = new vueInstance.$phoenix.Socket(FQDN, {
_token: token
});
socketHandler.onOpen(() => VueBus.$emit('SOCKET_OPEN'));
//...... Vue component (example)
VueBus.$on('SOCKET_OPEN', function () {
let chan = VueStore.socketHandler.channel('PRIV_CHANNEL', {
_token: token
});
chan.join()
.receive('ok', () => {
//... code
})
})
The above is an example, we have a Vuex store for a global state (socket etc), centralized message bus (Vue app) for communicating between components and channel setups which come from the instantiated Phoenix Socket.
Our channel setup relies on an authenticated Socket connection which needs authentication itself to join that particular channel.
The question is, is this even possible with Cycle.js?
Initialize WebSocket connection with token parameters from a REST call (JWT Token response) - we have implemented this partially
Create channels based off that socket and token (channel streams off a driver?)
Accessing multiple channel streams (I am assuming it may work like sources.HTTP.select(CATEGORY))
We have a 1: N dependency here which I am not sure is possible with drivers.
Thank you in advance,
Update# 17/12/2018
Essentially what I am trying to imitate is the following (from Cycle.js.org):
The driver takes a sink in, in order to perform write effects (sending messages on a specific channels) but also may return a source; this means there are two streams which are async? Which means that creating the socket at runtime may lead to one stream accessing the "socket" before it is instanitated; please see comments in the snippet below.
import {adapt} from '#cycle/run/lib/adapt';
function makeSockDriver(peerId) {
// This socket may be created at an unknown period
//let socket = new Sock(peerId);
let socket = undefined;
// Sending is perfect
function sockDriver(sink$) {
sink$.addListener({
next: listener => {
sink$.addListener({
next: ({ channel, data }) => {
if(channel === 'OPEN_SOCKET' && socket === null) {
token = data;
// Initialising the socket
socket = new phoenix.Socket(FQDN, { token });
socketHandler.onOpen(() => listener.next({
channel: 'SOCKET_OPEN'
}));
} else {
if(channels[channel] === undefined) {
channels[channel] = new Channel(channel, { token });
}
channels[channel].join()
.receive('ok', () => {
sendData(data);
});
}
}
});
},
error: () => {},
complete: () => {},
});
const source$ = xs.create({
start: listener => {
sock.onReceive(function (msg) {
// There is no guarantee that "socket" is defined here, as this may fire before the socket is actually created
socket.on('some_event'); // undefined
// This works however because a call has been placed back onto the browser stack which probably gives the other blocking thread chance to write to the local stack variable "socket". But this is far from ideal
setTimeout(() => socket.on('some_event'));
});
},
stop: () => {},
});
return adapt(source$);
}
return sockDriver;
}
Jan van Brügge, the soluton you provided is perfect (thank you) except I am having trouble with the response part. Please see above example.
For example, what I am trying to achieve is something like this:
// login component
return {
DOM: ...
WS: xs.of({
channel: "OPEN_CHANNEL",
data: {
_token: 'Bearer 123'
}
})
}
//////////////////////////////////////
// Some authenticated component
// Intent
const intent$ = sources.WS.select(CHANNEL_NAME).startWith(null)
// Model
const model$ = intent$.map(resp => {
if (resp.some_response !== undefined) {
return {...}; // some model
}
return resp;
})
return {
DOM: model$.map(resp => {
// Use response from websocket to create UI of some sort
})
}
first of all, yes this is possible with a driver, and my suggestion will result in a driver that feels quite like the HTTP driver.
First of all to have some rough pseudo code that where I can explain everything, I might have misunderstood parts of your question so this might be wrong.
interface WebsocketMessage {
channel: string;
data: any;
}
function makeWebSocketDriver() {
let socket = null;
let token = null;
let channels = {}
return function websocketDriver(sink$: Stream<WebsocketMessage> {
return xs.create({
start: listener => {
sink$.addListener({
next: ({ channel, data }) => {
if(channel === 'OPEN_SOCKET' && socket === null) {
token = data;
socket = new phoenix.Socket(FQDN, { token });
socketHandler.onOpen(() => listener.next({
channel: 'SOCKET_OPEN'
}));
} else {
if(channels[channel] === undefined) {
channels[channel] = new Channel(channel, { token });
}
channels[channel].join()
.receive('ok', () => {
sendData(data);
});
}
}
});
}
});
};
}
This would be the rough structure of such a driver. You see it waits for a message with the token and then opens the socket. It also keeps track of the open channels and sends/receives in those based on the category of the message. This method just requires that all channels have unique names, I am not sure how your channel protocol works in that regard or what you want in particular.
I hope this enough to get you started, if you clarify the API of the channel send/receive and the socket, I might be able to help more. You are also always welcome to ask questions in our gitter channel