Search code examples
windowsperlinternet-explorerperlscript

Why are writes to an html page buffered in Internet Explorer? Is there a way around it?


I've written a very small html page for IE that uses client-side PerlScript. In the script, I launch a separate process which creates a TCP socket. After launching the process, I start a listener which appends HTML to the end of a div as lines are read, additionally for debugging, I added alerts as each line is read.

The problem is that the alerts hit spot on as lines are read, but the HTML doesn't update until the process has finished. Why is that? Is there a way to force the page to render so that I can get the same behavior?

Here's the page:

<html>
    <head>
        <title>Message Test</title>
        <script language=perlscript>
            use util;
            use Win32::Process;

            my $alreadyrunflag = 0;

            sub _write {
                $window->document->body->insertAdjacentHTML("beforeEnd", $_[0]);
            }

            sub go {
                _write("starting!<br/>");

                my $port = 12345;
                #//system("start /b /NORMAL perl C:/development/plslog/clitest2.pl $port");
                Win32::Process::Create(my $Proc, "C:/perl/bin/perl.exe", "perl clitest2.pl 12345", 1, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS | DETACHED_PROCESS, "C:/development/plslog");
                $window->alert("Hi! started!");

                if (defined (my $INPUT = util::CreateClient($port))) {
                    while(defined (my $line = <$INPUT>)) {
                       _write($line);
                        sleep(3);
                    }
               }

               _write("ending!");
            }

            $window->setTimeout("go()",0);
        </script>
    </head>
    <body>
        <div id="content"></div>
    </body>
</html>

I have looked at on-line documentation for PerlScript which is incredibly sparse. I'm looking for hints at how to command IE to fire page renders, respond to these html insertions... etc.

In researching this, I have found that I can get it to render as the lines are read if instead of onload, I use an onclick on the div. Perhaps I'm not using the right event and the page isn't fully rendered before the subroutine is fired? I'm very frustrated with IE here (v. 9).

And yes, when I use this in a command line situation, everything fires as expected. The CreateServer/CreateClient are just wrappers around IO::Socket::INET for client and server.

UPDATE (based @Oleg V. Volkov's answer):

I modified go and added a _read() function that reads a line, then passes control back to the browser to render. Seems to do an ok job.

sub _read() {
  my $port = shift;
  $INPUT = util::CreateClient($port) if ! $INPUT;
  if (defined (my $line = <$INPUT>)) {
    _write($line);
    $window->setTimeout("_read($port)", 0);
  }
  else {
    _write("Fin!");
  }
}
sub go() {
  _write("starting!<br/>");
  my $port = 12345;
  Win32::Process::Create(my $Proc, "C:/perl/bin/perl.exe", "perl clitest2.pl $port", 1, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS | DETACHED_PROCESS, "C:/development/plslog");
  $window->alert("Hi! started!");
  $window->setTimeout("_read($port)", 0);
}

That seems to get near real-time updates.


Solution

  • Most (if not all) browsers do not redraw page until you return from hosted script process. They can reflow internal representation of data on some operations, but still won't spend time redrawing it until you've returned control. To allow browser to redraw, you should break your task into steps and yield once in a while by scheduling next step with setTimeout and returning from your script.

    In your particular case, replacing sleep(3), where you do nothing anyway, with scheduling of next loop iteration to happen 3 seconds later, seems like the best way.