So, I create a port
var port = chrome.runtime.connectNative("my.native.app");
And I'll define
port.onMessage.addListener(onNativeMessage);
port.onDisconnect.addListener(onDisconnect);
So when I call
port.postMessage({"text":"messsage"});
It goes to my native application using standard in/out and I get my response.
Here's my problem: I have several functions that expect a response, and others that do not. I want to be able to post a message and wait for a response from the native application (which is continually running). How is this done?
I am aware of "one time messaging" via sendMessageNative which works great, except I use my native application as a state machine, so it kills my application after it is done, which is no good.
You could add another listener to onNativeMessage to call your callback and then de-register.
Something like this, with closures:
function callbackOnId(ev, id, callback) {
var listener = ( function(port, id) {
var handler = function(msg) {
if(msg.id == id) {
ev.removeListener(handler);
callback(msg);
}
}
return handler;
})(ev, id, callback);
ev.addListener(listener);
}
/* ... */
callbackOnId(port.onMessage, "someRequestId", callback);
port.postMessage({"text":"message", "id": "someRequestId"});
Now, the first time your port receives a message containing "id": "someRequestId", callback will be called with that message, after which the listener will de-register itself.
If you need it, you can add an ability to remove the listener and/or modify the message checks.
My solution is to assign each message an unique id, and use a map to store the callback function for each message id (if there is a callback for the message).
When you receive a message from the host, check the message id and lookup the callback map. If there is a callback bound to that message, pass the response data and call it!
Related
I'm sending data between a parent window and an embedded <iframe> using window.postMessage(). The parent window listens for a "mounted" message from the <iframe>, and sends a response message with a JavaScript object model as the data.
Here's the parent window event listener for the "mounted" message:
window.addEventListener('message', (event) => {
if (hasOwnProperty.call(event.data, 'mounted')) {
$('iframe')[0].contentWindow.postMessage({ model }, '*');
}
});
The <iframe> may send the "mounted" message several times in one session, and the parent window needs to respond with model each time.
However, on the initial page load, the parent window asynchronously retrieves model from an external API, which could resolve before or after the parent window receives a "mounted" message.
let model = {};
axios.get('/api/model')
.then((response) => {
model = response.data;
});
So, if the parent window received a "mounted" message before the model exists, I want my code to wait until model does exist and then send the response. How can I achieve this? I assume it's going to involve changing my postmessage({ model }, '*') call into a Promise, but I'm not sure how to attach that Promise to make it wait for the axios.get call to complete.
I'm aware of how Promise.all() works, but that wouldn't really work since the "mounted" message can be sent multiple times in a window session.
One solution
While writing this question, I realized that I could write a function that sends model only if model isn't empty, which both my "mounted" event listener and my asynchronous data retrieval could call. So, the message is always posted when the data is loaded from the external API (and it doesn't matter if the <iframe> hasn't mounted), and the event listener only responds once the model has loaded. Here's the complete code for the solution:
function postModel(model) {
if (!_.isEmpty(model)) {
$('iframe')[0].contentWindow.postMessage({ model }, '*');
}
}
let model = {};
axios.get('/api/model')
.then((response) => {
model = response.data;
postModel(model);
});
window.addEventListener('message', (event) => {
if (hasOwnProperty.call(event.data, 'mounted')) {
postModel(model);
}
});
This works for my purposes, but I'd still be interested to see if there is an async/await approach that would be better here.
Maybe you would find want you need with frameTalk.js.
FrameTalk supports Promises between iframes. So, you would do smth like that
frameTalk.sendPromise(window.top, "myFrameID", "methodNameToRun", ["methodAttributesToPass"])
.then(doOnSuccess, doOnFail);
see more at
https://github.com/inedu/frameTalk
I am new to express and am trying to wrap my head around callbacks in RESTful actions. In my PUT request below, I'm confused about the following line that I have bolded below. Why is response.pageInfo.book being set to the second parameter in the anonymous function (result)? that seems kind of arbitrary.
Also, what is the best way to inspect some of these parameters (req, res, result, etc)? When I console.log it, doesn't show up in my terminal or in my browser console.
exports.BookEdit = function(request, response) {
var id = request.params.id;
Model.BookModel.findOne({
_id: id
}, function(error, result) {
if (error) {
console.log("error");
response.redirect('/books?error=true&message=There was an error finding a book with this id');
} else {
response.pageInfo.title = "Edit Book";
**response.pageInfo.book = result;**
response.render('books/BookEdit', response.pageInfo)
}
})
}
The findOne function takes a query ({_id : id}) and a callback as arguments. The callback gets called after findOne has finished querying the database. This callback pattern is very common in nodejs. Typically the callback will have 2 arguments
the first one error is only set if there was an error.
the second one usually contains the value being returned. In this case you are finding one book in the database.
The line you have bolded is where the book object is assigned to a variable which will be sent back to be rendered in the browser. It is basically some javascript object.
Your second request, to debug this stuff, here is what you can do:
In you code type the word debugger;
e.g.
var id = request.params.id;
debugger;
Next, instead of running your program like this:
node myprogram.js
... run with debug flag, i.e.
node debug myprogram.js
It will pause at the beginning and you can continue by pressing c then Enter
Next it will stop at that debugger line above. Type repl and then Enter and you'll be able to inspect objects and variables by typing their names.
This works very well and requires no installation. However, you can also take a more visual approach and install a debugger such as node-inspector which does the same thing but in a web browser. If you use a good IDE (e.g. webstorm) you can also debug node.js pretty easily.
In the above, the document that is the result of the findOne() query is being added to the pageInfo key of the response and is then being rendered in a template. The first parameter is a potential error that must be checked and the remainder contain data. It's the standard node idiom that an asynchronous call returns to a callback where you do your work.
The writer of the code has also decided to decorate the response object with an extra attribute. This is often done when a request passes through a number of middleware functions and you might want to build up the response incrementally (for example having a middleware function that adds information about the current user to the pageInfo key).
Look and see what else is on response.pageInfo. Information was probably put there by previous middleware (especially since the function above expects the pageInfo key to exist). Just do a console.log(response.pageInfo) and look on your server log or standard out.
I am having a bit of trouble with creating one function. I have two sockets. I want to write to one, which should trigger the other to begin output. I want to wait until receiving a specific reserved token to return, synchronously, and unbind the event listener.
execute: function (command) {
check(command, String);
// wait for the "okay" message on the messages socket
this._messages.on('data', Meteor.bindEnvironment(function (data) {
// if nothing comes back in 10 seconds, time out.
Meteor.setTimeout(function () {
var parse = data.toString('ascii').split(':');
if (parse[0] === 'End') {
// Receiving 'End' means the command was successful unbind this event handler and return
return 0;
}
}, 10000);
return 1;
}));
// send the command to the command socket
this._commands.write(`XQ#${command}\r`);
}
The problem I am having is forcing it to block until it receives this data, then unbinding the callback when the appropriate data is received or time out occurs.
I might be doing this wrong. It does kind of seem like unregistering a callback from... within the callback is basically like lifting a chair that you're already sitting on... but what is the appropriate way to handle this use case?
I'm currently trying to rebroadcast my local stream to all my peer connections. options I tried:
1) Loop trough all my peer connection and recreate them with the new local stream. Problem that I encounter here is the fact that createOffer is asynchronous.
2) create 1 sdp and send it to all peers. Problem: no video
Would anyone have a way to resend an offer to a list of peers?
Each PC needs to recreate an offer (as bwrent said).
as you obviously are using a p2p multiparty (multiple peer connections) you might want to pass on the peerID to the createOffer success callback every time, then you don't have to worry about it being asynchronous. You need to make the full handshake (offer, answer, candidate) peerID dependent.
(Simplified) Example from our SDK
Skyway.prototype._doCall = function (targetMid) {
var pc = this._peerConnections[targetMid]; // this is thread / asynchronous safe
pc.createOffer(
function (offer) {
self._setLocalAndSendMessage(targetMid, offer); // pass the targetID down the callback chain
},
function (error) {this._onOfferOrAnswerError(targetMid, error);},
constraints
);
};
Skyway.prototype._setLocalAndSendMessage = function (targetMid, sessionDescription) {
var pc = this._peerConnections[targetMid]; // this is thread / asynchronous safe
pc.setLocalDescription(
sessionDescription,
self._sendMessage({ target: targetMid, ... }), // success callback
function () {} // error callback
);
};
If you mean async in a way that when a callback fires it has the wrong variable of who to send it to as the loop has ended and the variable contains the last 'person'? You could scope it to solve the asynchronous problem:
For(var i=0;i<peerConnections.length;i++){
(function(id){
//inside here you have the right id. Even if the loop ended and the i variable has changed to something else, the I'd variable still is the same.
})(i);
}
This is a bit like Alex' answer, as his anwer also describes an example of scoping the variable inside the function executing the .createOffer
Another way to handle this correctly is to use renegotiation. Whenever you change a stream, the on onnegotiation event handler is automatically fired. Inside this function you create a new offer and send that to the other person. As you mentioned you have multiple peer connect ions listening to the stream, you need to know whom to send the sdp to. If you would add the persons id to the rtc object, you can then get it back inside the onnegotioation event by calling this.id.
I'd like to achieve the following behavior. Suppose there is a button on the client side which triggers a function that sends a message via a websocket. The first time this function is called, a WebSocket instance is created and then used in the future calls.
Creating a WebSocket instance is non-blocking. The constructor immediately returns a WebSocket object, starting the handshake in the background. A successful connection triggers the onopen callback.
A problem shows up when the second call comes in and the websocket is still performing the handshake (e.g. user makes double click on the button). All the messages need to be queued and sent when the websocket completes the handshake.
The following piece of code uses jQuery and a custom event to gather all messages received during the handshake.
var ws = null;
function sendMessage(message) {
if (!ws) {
ws = new WebSocket("ws://example.com");
ws.onopen = function() {
$(document).trigger('handshakeComplete');
}
}
if (ws.readyState == WebSocket.CONNECTING) { // *
$(document).bind('handshakeComplete', message, function(event) {
ws.send(event.data);
});
} else if (ws.readyState == WebSocket.OPEN) {
// use the persistent connection
ws.send(message);
}
}
It is possible the condition at the starred line to be evaluated as true and in that moment the websocket passes in the OPEN state, the onopen callback is executed and only after that the current message is added in the queue waiting for the handshakeComplete event. This would result in the loss of messages.
I would like to avoid this and I would appreciate any ideas, comments or suggestions.
I think you're worried that handshakecomplete could fire between the test (== CONNECTING) and when the bind() happens, and the message for the handshakecomplete would never be sent.
In theory if all that existed was JS, this would be wrong, since the event wouldn't fire until the current code finished. However, in reality the event may be generated on another thread in the background, and merely be run in the foreground after we reach a stable state.
The answer is to not embed the message in the handshake event handler. I suggest adding the message to a queue/array, and in the onopen handler process the array. The onopen event can fire (get queued) while we're executing, but it won't run until we hit a stable state (where all the messages are in the queue/array).