I'm using a JS library to stream server-sent-events on my html page:
<html>
<textarea rows='14' id="value" placeholder="anything you want here"></textarea>
<button type="button" onclick="post(clip)">get</button>
</html>
<script type="text/javascript" src="sse.js"></script>
<script>
url = "http://ac6ba97b046a5dcc677e.elb.us-east-1.amazonaws.com/myapi";
let textArea = document.getElementById("value");
function clip(){
s = textArea.value;
s = s.slice(0, -5);
textArea.value = s;
console.log('hello');
}
function post(callback){
var v = String(textArea.value);
console.log(v);
var source = new SSE(url, {
method: 'POST',
headers: { 'Content-Type': 'text/plain' },
payload: v
});
var arr = [];
source.addEventListener('message', function (e) {
arr.push(e.data);
textArea.value = arr.map(el => el || " ").join('');
});
source.stream();
callback();
}
</script>
When the button is clicked, data is sent to a server using POST method and the textbox is populated with data received from the server. I would like to clip the text in the textbox with clip()
after the post()
function is executed. Execution process must be like this:
1. post() logs textArea value
2. source.stream() is executed, textbox populated
3. clip() clips last 5 characters and logs 'hello'
But I instead get this:
1. post() logs textArea value
2. clip() clips last 5 characters and logs 'hello'
3. source.stream() is executed, textbox populated
For some reason clip()
is being executed before source.stream()
even after adding a callback.
The sse.js file that I'm using.
[EDIT] After moving callback()
to the end of 'message' handler, the issue still persists:
function post(callback){
var v = String(textArea.value);
console.log(v);
var source = new SSE(url, {
method: 'POST',
headers: { 'Content-Type': 'text/plain' },
payload: v
});
var arr = [];
source.addEventListener('message', function (e) {
arr.push(e.data);
textArea.value = arr.map(el => el || " ").join('');
callback();
});
source.stream();
}
Does anyone know what might be causing this?
When your script calls source.stream();
, it is doing an XMLHttpRequest.send()
operation, which is async by default.
So, what is happening:
post()
is calledcallback()
(i.e. clip()
) is calledmessage
event handler is calledtextArea.value
is setLuckily the fix is simple: you only want callback()
to be called when a message is received. So move callback() to be at the end of the message
event handler, not at the end of post()
.
It will do this after every message event that is received. If you only wanted it to happen after the first event, you will need to implement some logic to keep track of how many events have been received. (And if there will only be one message event, you should be using an Ajax call, not an SSE/EventSource call.)
UPDATE: Discussion in the comments are starting to get out of scope of your original question (where the answer is, simply put, "it is async, not sync"). But I think it is worth pointing out here that you set up a new SSE object every time the user clicks the button. Each SSE object will have its own dedicated TCP/IP socket and its own listener function. This is (generally) not a good idea: instead create the SSE connection once at the start of your web app.
And, though your SSE
polyfill library allows using POST, the standard SSE does not. If you only want the app to poll the server when the user presses the button, consider switching from using SSE to normal AJAX.