Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
150 views
in Technique[技术] by (71.8m points)

javascript - JS SSE callback isn't working with EventListener

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?

question from:https://stackoverflow.com/questions/65862490/js-sse-callback-isnt-working-with-eventlistener

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

When your script calls source.stream();, it is doing an XMLHttpRequest.send() operation, which is async by default.

So, what is happening:

  1. user clicks, and post() is called
  2. SSE object and its event listener is set up
  3. source.stream() is called, which sends the request to the server.
  4. callback() (i.e. clip()) is called
  5. Server sends back a response
  6. Your message event handler is called
  7. textArea.value is set

Luckily 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...