Home > JavaScript, Object Pascal, OP4JS, Smart Mobile Studio > Smart Mobile Studio, workers by code tip

Smart Mobile Studio, workers by code tip

Web workers work for you!

Web workers work for you!

Webworkers are essentially the browsers answer to classical threads. Although I cant for the life of me understand why the W3C have gone to such lengths to utterly screw up their standard, I can do my best to rectify their mess with easy Smart functions 🙂

In short, here is the challenge: Web workers must be stored in a separate file. So you can’t tell the browser to just take an object or method and “use that as a thread”. I wish you could do that, it would make my life easier – but sadly you can’t. You are expected to have separately compiled JavaScript worker-files, which know nothing about the main application. That last part is actually quite OK, since you don’t want multiple threads messing up your GUI.

The second challenge is that you can’t even share variables. All communication between a worker and your main application must be performed via messaging. So a worker can only receive and send messages. To start some “work” you essentially send over a text-based command (use JSON to stringify records for instance) telling the worker what to do.

Creating workers by code

Turns out there are legal ways of bypassing the first Webworker rules, namely that of a separate file. You can write your script to a blob (binary large object) and then use the URL composer to generate a URL for your object. This will be a unique resource locator which the browser can use to load data from. In short, here is how you do it:

function makeWebWorker(Script:String):THandle;
var
  URL:    Variant;
  BLOB:   Variant;
  WORKER: Variant;
  mTemp:  Variant;
begin
  asm
    @URL    = window.URL || window.webkitURL;
    @BLOB   = window.Blob || window.webkitBlob;
    @WORKER = window.Worker || window.webkitWorker;
    var @mTemp = new @BLOB([@Script]);
    @result = new @WORKER(( @URL).createObjectURL(@mTemp));
  end;
end;

You can now create as many workers as you like by code, like this:

// create the worker
var FWorker:=makeWebWorker(
  #"onMessage: function (msg) {
    postmessage(msg);
   }");

// setup message handler for when the worker sends us messages
FWorker.onMessage := procedure (msg:string)
 begin
  writeln("Message back from worker!");
  writeln(msg);
 end;

// send message to worker
FWorker.postmessage("Hello this is a test!");

And now you are probably thinking — oh why can’t i write Smart Pascal worker code?
Well there are several reasons for that. First of all, since the worker is 100% isolated it means that it wont recognize the RTL. The RTL would have to be loaded into the worker in order to function properly. And even then there are problems, because the VMT would be different between the main program and the worker program.

However, a completely clean, non RTL dependent piece of smart pascal will work! But the only way to get that done is to isolate it in a separate file.

But the trick above is handy, especially if all you want to do is check for features without interrupting the main program. And if you know JS fairly well you can also write some serious message-handling in pure JS which can be reflected by objects on the other side.

Another example

Below if a fully-blown example which successfully establishes a worker, installs a message-handler on both ends, and sends a message through. And it’s all done from the same source-code. Please note that getting the source of a function does not work on class members, only stand-alone functions.

Enjoy the code!

function makeWebWorker(Script:String):THandle;
var
  URL:    Variant;
  BLOB:   Variant;
  WORKER: Variant;
  mTemp:  Variant;
begin
  asm
    @URL    = window.URL || window.webkitURL;
    @BLOB   = window.Blob || window.webkitBlob;
    @WORKER = window.Worker || window.webkitWorker;
    var @mTemp = new @BLOB([@Script]);
    @result = new @WORKER(( @URL).createObjectURL(@mTemp));
  end;
end;

procedure TestCode;
var
  mContext: THandle;
begin
  asm
    @mContext = this;
  end;
  mContext.onmessage := procedure (msg:variant)
    begin
      asm
      console.log(@msg);
      end;
    end;
end;

procedure TForm1.W3Button3Click(Sender: TObject);
var
  mRef: TProcedureRef;
  mCode:  String;
begin
  mRef:=@testcode;
 asm
  @mCode = @mref;
 end;
 mCode += "TestCode();";
 var mWorker := makeWebWorker(mCode);
 mWorker.onmessage:=Procedure (msg:variant)
  begin
    writeln("We got a message back:" + String(msg.data));
  end;
 mWorker.onerror:=procedure (msg:Variant)
 begin
  showmessage("An error occured!");
 end;
 mWorker.postMessage("Hello dude!");
end;
Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: