I have a function, that gets called twice by an observe function. As you can see, in the addMessage function, there is a check that if it is called with the same messge, it returns.
My problem is, that the addMessage function is getting called twice with the same message.
I think the reason is because it is being done quickly before the first message has been added, the second message is executed already.
What is the best way to synchronize the messages, that the first one is given time to save, before the second one is processed?
this.messages.observe({
changed: (newMessage, oldMessage) => this.addMessage(newMessage)
});
and
private addMessage(message: Message): void {
let foundMessage: Message = this.localMessageCollection.findOne({ _id: message._id });
if (foundMessage && foundMessage._id === message._id) {
console.log('addMessage(found): '+message._id+' '+message.content);
return;
}
console.log('addMessage: '+message._id+' '+message.content);
this.addLocalMessage(message);
this.chatsStorageService.addMessage(this.activeChat, message).then((messageData: Message) => {
let data = {
chat: this.activeChat,
messageString: this.messageString,
sendMessage: true
}
this.events.publish('messages:update', data);
});
}
Related
I am new to Angular and RxJS. I am analysing the following extract from the Angular tutorial (ng-book2-book-angular-11-r77-code).
My question is when the call this.messages = this.updates... (in the constructor method) executes - is it executing in the constructor or when addMessage (message: Message) is called?
import { Injectable } from '#angular/core';
import { Subject, Observable } from 'rxjs';
import { User } from '../user/user.model';
import { Thread } from '../thread/thread.model';
import { Message } from '../message/message.model';
const initialMessages: Message[] = [];
interface IMessagesOperation extends Function {
(messages: Message[]): Message[];
}
#Injectable()
export class MessagesService {
// a stream that publishes new messages only once
newMessages: Subject<Message> = new Subject<Message>();
// `messages` is a stream that emits an array of the most up to date messages
messages: Observable<Message[]>;
// `updates` receives _operations_ to be applied to our `messages`
// it's a way we can perform changes on *all* messages (that are currently
// stored in `messages`)
updates: Subject<any> = new Subject<any>();
// action streams
create: Subject<Message> = new Subject<Message>();
markThreadAsRead: Subject<any> = new Subject<any>();
constructor() {
this.messages = this.updates
// watch the updates and accumulate operations on the messages
.scan((messages: Message[],
operation: IMessagesOperation) => {
return operation(messages);
},
initialMessages)
// make sure we can share the most recent list of messages across anyone
// who's interested in subscribing and cache the last known list of
// messages
.publishReplay(1)
.refCount();
// `create` takes a Message and then puts an operation (the inner function)
// on the `updates` stream to add the Message to the list of messages.
//
// That is, for each item that gets added to `create` (by using `next`)
// this stream emits a concat operation function.
//
// Next we subscribe `this.updates` to listen to this stream, which means
// that it will receive each operation that is created
//
// Note that it would be perfectly acceptable to simply modify the
// "addMessage" function below to simply add the inner operation function to
// the update stream directly and get rid of this extra action stream
// entirely. The pros are that it is potentially clearer. The cons are that
// the stream is no longer composable.
this.create
.map( function(message: Message): IMessagesOperation {
return (messages: Message[]) => {
return messages.concat(message);
};
})
.subscribe(this.updates);
this.newMessages
.subscribe(this.create);
// similarly, `markThreadAsRead` takes a Thread and then puts an operation
// on the `updates` stream to mark the Messages as read
this.markThreadAsRead
.map( (thread: Thread) => {
return (messages: Message[]) => {
return messages.map( (message: Message) => {
// note that we're manipulating `message` directly here. Mutability
// can be confusing and there are lots of reasons why you might want
// to, say, copy the Message object or some other 'immutable' here
if (message.thread.id === thread.id) {
message.isRead = true;
}
return message;
});
};
})
.subscribe(this.updates);
}
// an imperative function call to this action stream
addMessage(message: Message): void {
this.newMessages.next(message);
}
}
In the following example, lazyNumber is assigned in the constructor and referenced in the printNumber method.
The expression isn't evaluated to 4 until it's called.
class a {
constructor(){
this.lazyNumber = () => 5 - 1;
}
printNumber(){
console.log( this.lazyNumber() );
}
}
The same fundamental thing is happening in your example.
You example is defining an attribute and the code that get runs when it's "called". addMessage is emitting a new value on the observable, that causes the listeners assinged above to react accordingly
I am facing a problem that the broadcast channel is not listening sometime in useEffect. There is some list and upon clicking individual item, the broadcast postmessage should be emitting message to the listener of another file. Sometime it's working but for some reason, it's not receiving that event.
This snippet is located at index.js. Upon changes happened in dependencies, I am getting debug message all the time however it's not going inside onMessage.
useEffect(() => {
const messageBus = new BroadcastChannel('channelABC');
console.debug("ITS COMING HERE ALL THE TIME");
const onMessage = ({ data: message }) => {
if (message.type === 'ABCFired') {
showABC(message);
}
};
messageBus.addEventListener('message', onMessage);
return () => {
messageBus.removeEventListener('message', onMessage);
messageBus.close();
};
}, [dependencies]);
This is another file where message is being emitted.
const onRowClicked = index => {
setId(oldId => {
if (oldId !== index) {
console.debug("ITS ALSO WORKING");
messageBus.postMessage({ type: 'ABCFired', message });
return index;
}
else {
return null;
}
});
};
I am searching through the same answer right now. I have found that maybe when the broadcast channel shoots a message, the component gets unmount and the event listener is removed.
So by the time the message is fired, there is no listener.
Make a console statement in return function of use effect called
console.log('unmount)
You already have a console in onRowClicked, so check when you fire, onRowClicked, do you get unmount after that or something.
Quickfix
Remove the dependency from use Effect. In your case you want to enable eventListner once the app starts, I think.
I have a pop-up message that I want to present when my observable is delivered, its a string observable.
this is my function that returns string observable:
public sendUpdate() {
this._mtService.sendCurrentUpdate(this.myList).subscribe(
res => this.messageToDisplay = res,
error => this.messageToDisplay = error
);
}
this is my function that I present the pop up with the relevant message:
public showMessageDialog(message) {
let config = new MdDialogConfig()
.title('Message:')
.textContent(message)
.ok('Got it');
this.dialog.open(MdDialogBasic, this.element, config);
}
now, I want to know where and how should I call this message to present messageToDisplay when the observable is ready.
I t would be even better if you can tell me how I can show some loader while the observable is waiting to receive the string and then when its there present it...
I tried to do this:
public sendUpdate() {
this._mtService.sendCurrentUpdate(this.myList).subscribe(
res => this.messageToDisplay = res,
error => this.messageToDisplay = error
);
this.showMessageDialog(this.messageToDisplay);
}
but what happens here is that the first time i click on update i see an empty pop-up and if I click on it again I see the pop-up with the message, its obvious that it happens because the string didnt came back yet, but how do I get over it?
thanks!
The functions you pass to subscribe() will be called later/asynchronously, hence you need to call showMessageDialog() later as well:
public sendUpdate() {
this.showLoader();
this._mtService.sendCurrentUpdate(this.myList).subscribe(
res => { this.stopLoader(); this.showMessageDialog(res); },
error => { this.stopLoader(); this.showMessageDialog(error); }
);
}
Hello Stackoverflow community,
In nuts
I am wondering why the insert callback is not being called async properly as the documentation says, having a code like:
Meteor.methods({
addUpdate: function (text) {
Updates.insert({
text: text,
createdAt: new Date(),
owner_email: Meteor.user().emails[0].address,
owner_username: Meteor.user().username
}, function(e, id) {
debugger; //<-- executed first with 'e' always undefined
});
debugger; //<-- executed after
}
});
the debugger inside the callback function is executed before the debugger afterwards, if the function is async the debugger inside the callback should be called at the end right?
More info
I am very new with meteor, the thing is that I am trying to make an small app, and experimenting, by now I wanted to confirm what I had understood about some concepts in this case the "insert" method. given the following code:
lib/collections/updateCollection.js
Update = function (params, id) {
params = params || {};
// define properties for update model such as text
this._text = params.text;
}
Update.prototype = {
// define some getters and setters, such as doc
get doc() {
return {
createdAt: this.createdAt,
text: this.text,
owner_email: this.owner_email,
owner_username: this.owner_username
};
},
notify: function notify(error, id) {
var client, notification, status;
client = Meteor.isClient ? window.Website : false;
notification = (client && window.Hub.update.addUpdate) || {}
status = (!error && notification.success) || notification.error;
if (client) {
return client.notify(status);
}
}
save: function save(callback) {
var that;
that = this;
callback = callback || this.notify;
Updates.insert(that.doc, function (error, _id) {
that._id = _id;
callback(error, _id); <-- here is the deal
});
}
}
lib/methods/updateService.js
updateService = {
add: function add(text) {
var update;
update = new Update({
text: text,
createdAt: new Date(),
owner_email: Meteor.user().emails[0].address,
owner_username: Meteor.user().username
});
update.save();
},
// methods to interact with the Update object
};
lib/methods/main/methods.js
Meteor.methods({
addUpdate: function (text) {
updateService.add(text);
}
});
My expectations here is when the client do something like Meteor.call('addUpdate', text); and everything is cool, a successful message is shown, otherwise the error is "truth" and an error message is shown. What is actually happening is that the callback is always called with error undefined (like if everything es cool), the callback also is not being called async, it is just called directly.
Even when I turn off the connection the update insertion shows a success message.
Any idea? maybe my app structure is making meteor work wrong? I really do not know. Thanks in advance.
Your code is executing inside a method. On the client, methods are executed simply to simulate what the server will do before the server responds (so that the app seems more responsive). Because the DB changes here are just simulating what the server is already doing, they are not sent to the server, and therefore synchronous. On the server, all code runs inside a Fiber, so it acts synchronous. (However, Fibers run in parallel just like normal callback-soup Node.)
I can return a value if I send a sync message:
// frame script
var chromeBtnText = sendSyncMessage("getChromeToolbarButtonText");
if (chromeBtnText == 'blah') {
alert('tool is blah');
}
// chrome script
messageManager.addMessageListener("getChromeToolbarButtonText", listener);
function listener(message) {
return document.getElementById('myChromeToolbarButton').label.value;
}
How do I achieve this with a callback with sendAsyncMessage?
I was hoping to do something like:
// frame script
function myCallback(val) {
var chromeBtnText = val;
if (chromeBtnText == 'blah') {
alert('tool is blah');
}
}
var chromeBtnText = sendAsyncMessage("getChromeToolbarButtonText", null, myCallback);
There is no callback for replies. In fact, there is no reply at all. The return value from the chrome message listener is simply ignored for async messages.
To do fully async communication, you'd have to send another message containing the reply.
Frame script
addMessageListener("getChromeToolbarButtonTextReply", function(message) {
alert(message.data.btnText);
});
sendAsyncMessage("getChromeToolbarButtonText");
Chrome
messageManager.addMessageListener("getChromeToolbarButtonText", function(message) {
var btnText = document.getElementById('myChromeToolbarButton').label.value;
// Only send message to the frame script/message manager
// that actually asked for it.
message.target.messageManager.sendAsyncMessage(
"getChromeToolbarButtonTextReply",
{btnText: btnText}
);
});
PS: All messages share a namespace. So to avoid conflicts when another piece of code wants to use the same name getChromeToolbarButtonText, you better choose a more unique name in the first place, like prefixing your messages with your add-on name my-unique-addoon:getChromeToolbarButtonText or something like that. ;)
I was also hoping to do something similar:
messageManager.sendAsyncMessage("my-addon-framescript-message", null, myCallback);
I'm going the other direction so the myCallback would be in chrome but it's exactly the same principle.
I'd used similar approaches to #Noitidart and #nmaier before but in this new case I wanted to bind to some local data so myCallback can behave differently based on the application state at the time the first message was sent rather than at the time the callback is executed, all while allowing for the possibility of multiple message round-trips being in progress concurrently.
Chrome:
let someLocalState = { "hello": "world" };
let callbackName = "my-addon-somethingUnique"; // based on current state or maybe generate a UUID
let myCallback = function(message) {
messageManager.removeMessageListener(callbackName, myCallback);
//message.data.foo == "bar"
//someLocalState.hello == "world"
}.bind(this); // .bind(this) is optional but useful if the local state is attached to the current object
messageManager.addMessageListener(callbackName, myCallback);
messageManager.sendAsyncMessage("my-addon-framescript-message", { callbackName: callbackName } );
Framescript:
let messageHandler = function(message) {
let responseData = { foo: "bar" };
sendAsyncMessage(message.data.callbackName, responseData);
};
addMessageListener("my-addon-framescript-message", messageHandler);
There's a real-world example here: https://github.com/luckyrat/KeeFox/commit/c50f99033d2d07068140438816f8cc5e5e290da9
It should be possible for Firefox to be improved to encapsulate this functionality in the built-in messageManager one day but in the mean-time this approach works well and with a surprisingly small amount of boiler-plate code.
in this snippet below. i add the callback before sendAsyncMessage('my-addon-id#jetpack:getChromeToolbarbuttonText'... as i know it will send back. Then I remove it after callback executes. I know I don't have to but just to kind of make it act like real callback, just to kind of show people, maybe it helps someone understand.
Frame:
/////// frame script
function CALLBACK_getChromeToolbarButtonText(val) {
removeMessageListner('my-addon-id#jetpack:getChromeToolbarButtonTextCallbackMessage', CALLBACK_getChromeToolbarButtonText); //remove the callback
var chromeBtnText = val;
if (chromeBtnText == 'blah') {
alert('tool is blah');
}
}
addMessageListener('my-addon-id#jetpack:getChromeToolbarButtonTextCallbackMessage', CALLBACK_getChromeToolbarButtonText); //add the callback
var chromeBtnText = sendAsyncMessage("my-addon-id#jetpack:getChromeToolbarButtonText", null);
Chrome:
////// chrome script
messageManager.addMessageListener("my-addon-id#jetpack:getChromeToolbarButtonText", listener);
function listener() {
var val = document.getElementById('myChromeToolbarButton').label.value;
sendAsyncMessage('my-addon-id#jetpack:getChromeToolbarButtonTextCallbackMessage',val);
}