Jump to content

lennart

Administrators
  • Content count

    363
  • Joined

  • Last visited

  • Days Won

    28

lennart last won the day on January 7

lennart had the most liked content!

1 Follower

About lennart

  • Rank

  • Birthday 09/21/1973

Contact Methods

  • Website URL
    jonlennartaasenden.wordpress.com

Profile Information

  • Gender
    Male
  • Location
    Vestfold, Norway

Business

  • Company name
    The Smart Company AS
  • Sector
    Information technology
  • Position
    Lead developer

Smart Mobile Studio

  • Edition
    Basic
  1. Save Content Dialog

    The node filesystem is wrapped. But right now im implementing filesystem classes, meaning that we will have a unified solution for this. Since storage is so different between node, browser, phonegap and various embedded hardware - it makes sense to create some "driver" like classes that expose a common interface. As for the filesystem units. The System.Filesystem.pas file is presently used by node.js only, but it will be absorbed by the drivers soon. The Filesystem.Memory unit is a fully working filesystem for a "ram disk". That you can also save to the cache. It gives you things like chdir, mkdir, dir, paths etc. etc. for a in-memory device. Then you can save the data to the cache. So this is the "driver" for the browser so to speak. A identical class is being written for nodejs. Both will be async (!) So heads up on that!
  2. back(s)lash

    Its not a "bug", the $ postfix / prefix means its a managed variable/field. Write a normal streamer or implement a ToJSON() mechanism. The mess of raw JS is what we are trying to get away from
  3. Database Examples

    Sorry for the delay we are working non-stop on the next update, so its hectic! First, let me explain a bit where we are, so we can pick the right storage mechanism: The past months we have focused solely on the visual aspect of the RTL. We had to start somewhere and since the visual aspect is what most people care about first, that seemed like the best place. So as you have no doubt seen, we have gone through the RTL with a fine tooth comb, removing things that didnt work, rewriting controls from scratch and testing each part to make sure that everything works. But that is just 1/3 of the path we are on. We are now moving into the non-visual aspects of the RTL. Here we have things like databases, non-visual controls (TComponent in Delphi and Lazarus), filesystems and ultimately node.js - which by nature is UI less and runs on the server (or the shell). We have also brushed up Phonegap and removed the wrongs we found there. The Phonegap API has not stood still and have evolved, so it was due for an overhaul. So that is a quick "status". We are completing 1/3 of the planned system, which covers the visual aspects. DB typically falls under "non-visual", with bindings back to visual later. DB for browsers, what is what? Modern browsers actually have 3 DB engines built into them: WebSQL IndexDB Microsoft Access (*) (*) Only supported by Microsoft and relies on COM (so not for mobile). It must be mentioned that WebSQL, which is the only really good option here, has been deprecated. But it wont vanish tomorrow. The W3C (world wide web consortium) is a bit like the vatican; they move in decades and code will linger in the codebase for ages before it changes. So I wouldnt worry too much about using WebSQL. Secondly, there are shim's for everything these days. A "shim" is a JS re-implementation of something. What a shim does is check if a feature is supported, and if it's not then the shim installs itself and delivers the missing functionality. In other words, you can safely use websql and just download a shim if suddenly google or firefox lacks it. Here is a nice shim for websql: https://github.com/agershun/WebSQLShim Then there is IndexDB. This is a no-sql database and is designed to store JS structures (or objects) in a typical name-value pair style. I think JS people like this a lot since they can easily stuff raw JS objects into it, using a string key. And while it does have its uses, its not a table/row/column solutions. And finally there is access. Which is a pure Microsoft solution, only runs in Edge or Internet Explorer and requires the onslaught of COM. This might have changed in the later versions of edge but either way - this is Microsoft only. So I doubt that is interesting. Thinking outside the box, Smart approach Facing this typical JavaScript mess (sorry but it's true, the browser wars were not kind to JS) I decided we needed to become independent. Browsers change all the time and JS have little standards to speak of compared to Delphi, C# or C++; So coming from Delphi JS will be hell to work with unless you do a long and serious study. Our role is in many ways to "tame" JS and force it to behave inside object pascal's rules. So we needed some structure, something that only change when we allow it! So that people can write programs without being afraid that it will vanish in six months. So we created two solutions: TW3Dataset for simple, single table work SQLite compiled from C to JavaScript Note: Some bugs sadly crept into TW3Dataset, but these have been fixed. So the next update will remedy the situation profoundly. The second option, SQLite, is awesome. It is a pure, 1:1 compile from the original C code, so it contains everything WebSQL has "and then some". The reason it havent been wrapped more rigidly is because it's destined to become a part of our "DataSnap" like API later. Like mentioned above we are on a journey here, and we have to deal with things in the right order. So each DB engine will be isolated in a "driver" like class and then register with a common API. That way people can use classes like TDatabase, TQuery, TStatement and similar high-level components to access any database they like. IndexDB is the oddball in all of this, so we might write a shim/proxy to leverage that. Picking an engine OK, with a situation rapport out of the way, lets pick a database. Our options are thus: WebSQL SQLite TW3Dataset Since TW3Dataset needs the update, I have excluded that. SQLite is awesome but could perhaps need more solid wrappers. So for this I will use WebSQL since that has more clearly defined classes. Picking a storage point Browsers only have cache storage. This is basically a sandboxed file that is saved in the cache folder. Under phonegap this is likewise sandboxed and stored in the "yourname.app/cache" folder if im not mistaken (not sure about that one, please check the Phonegap docs if you need the absolute path). But you dont need a path to load or save into the cache. The browser maps your data into the file-region automatically. Also worth mentioning: When you run in the browser you have a limit of 10-15 megabytes (depends on the browser) for cache storage. When you link the project with Phonegap this limit goes away and you have the same storage rights as native applications. So you dont need to worry about running out of space if you plan to link with phonegap. For pure browser work, you are not expected to make huge databases - but rather cache info before shipping that to a server. A bit like what you would use TClientDataset for under Delphi. But ok, let's use normal cache storage for now. WebSQL is excellent because this will store itself automatically! You dont need to do anything in particular to load or save it. It all boils down to you creating or dropping the database. DB example Right! So fire up Smart and create a new visual project. Our first business is to create the database object and I typically put that into the application class. That way it can be accessed by any form in the program. A nice helper function is also good to have. Here is how it looks so far: unit Unit1; interface uses Pseudo.CreateForms, System.Types, SmartCL.System, SmartCL.Components, SmartCL.Forms, SmartCL.Application, SmartCL.DbSql, Form1; type TApplication = class(TW3CustomApplication) private FDatabase: TW3WebSQLDatabase; public procedure ApplicationStarting; override; published Property Database: TW3WebSQLDatabase read FDatabase; end; // global easy-access function to DB layer function Datastore: TW3WebSQLDatabase; implementation function Datastore: TW3WebSQLDatabase; begin Result := TApplication(Application).database; end; procedure TApplication.ApplicationStarting; begin //setup database engine FDatabase:=TW3WebSQLDatabase.Create; FDatabase.DBName:={$I 'app:name'}; FDatabase.DBDescription:='Database for ' + {$I 'app:name'}; FDatabase.DBSize:=(1024 * 1024) * 4; inherited; end; end. This creates a websql database that can hold 4 megabytes of data. Which in browser terms is huge (I mean, you are not doing your taxes here are you). Then there is the main form and actually making something happen. So drop a TW3Button on the form and set the title to "Create Database". We then need some code to create a table and insert some records. For brewity here is the whole code for Form1: unit Form1; interface uses System.Types, System.Types.Convert, System.Objects, System.Time, SmartCL.System, SmartCL.Time, SmartCL.Graphics, SmartCL.Components, SmartCL.FileUtils, SmartCL.Forms, SmartCL.Fonts, SmartCL.Theme, SmartCL.Borders, SmartCL.Application, SmartCL.DbSql, SmartCL.Controls.Button; type TForm1 = class(TW3Form) procedure W3Button1Click(Sender: TObject); private {$I 'Form1:intf'} protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; end; implementation { TForm1 } uses unit1; procedure TForm1.W3Button1Click(Sender: TObject); var Transaction: TW3WebSQLCustomTransaction; begin // activate database try if not Datastore.Active then Datastore.Active := True; except on e: exception do begin writeln("Failed to activate database: "); writeln(e.message); exit; end; end; // Get new write transaction if Datastore.GetWriteTransaction(Transaction) then begin // Create our table try Transaction.Execute("create table if not exists customers (id integer primary key asc, name string);",[]); finally if Transaction.LastFailed then WriteLn(Transaction.LastError); Transaction.Free; end; end else raise Exception.Create('Failed to create write transaction error'); // Get new write transaction if Datastore.GetWriteTransaction(Transaction) then begin try // Populate the table with some records for var x := 1 to 10 do begin var MyData := 'John Doe #' + x.ToString(); Transaction.Execute("insert into customers (name) values (?);",[MyData]); end; finally if Transaction.LastFailed then WriteLn(Transaction.LastError); Transaction.Free; end; end else raise Exception.Create('Failed to create write transaction error'); // Get a new read transaction if DataStore.GetReadTransaction(Transaction) then begin // Setup the OnSuccess event handler. Transaction.OnSuccess := procedure (Sender: TObject) begin var Reader := TW3WebSQLReadTransaction(Sender); if Reader.Dataset <> nil then begin var Dataset := Reader.Dataset; for var x := 0 to Dataset.rows.Length - 1 do begin var Row := Dataset.rows.item(x); WriteLn(Row['name']); end; end; // All done, kill the transaction Reader.free; end; // Setup the OnFailed event handler TransAction.OnFailed := procedure (Sender: TObject) begin var Reader := TW3WebSQLReadTransaction(Sender); writelnF("Read transaction failed: %s", [reader.LastError]); // Kill the transaction Reader.free; end; // OK lets execute the SQL Query! try Transaction.Execute("select * from customers;",[]); finally if Transaction.LastFailed then WriteLn(Transaction.LastError); end; end; end; procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components end; procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} end; procedure TForm1.Resize; begin inherited; end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end. Now you are going to notice something -- namely that the database is persistent (!) So if you run this example 10 times, it will actually create 10 new records every time. The key here is the SQL that creates the database: Transaction.Execute("create table if not exists customers (id integer primary key asc, name string);",[]); This basically says "Create this database UNLESS it already exists". Manual storage with SQLite I am bit pressed for time right now. I will be away from the office for about a month pending surgery. I will be busy with forum duty etc. as much as I can, but sadly a secondary example with SQLite will have to wait a little. But I hope this one has given you an easy path to store things in the cache. Also remember: You can look at the data using the Browser Devtools I will do an article on SQLite when time allows
  4. back(s)lash

    Strange, no idea how you get the $ postfixing there (which means its a managed variable or object, it has nothing to do with obfuscation -- it just means smart handles the life-cycle of that variable or entity). Are you using inline variables to hold this or properly defined variables? There is a subtle but huge difference. But as EWB points out, you need to build it up like you would any array. And I would add some functions for each record to simplify managing them. Like "AddParam(bla bla bla)" and stuff like that. Just a quick test i did now to see what this was about: JC = record id : integer; external 'id'; name : string; external 'name'; age : integer; external 'age'; end; JB = record field1 : string; params : Array of JC; end; JA = record fields : array of JB; end; Then i just wrote some code in a button onClick handler: procedure Form1.W3Button1OnClick(Sender: TObject) var head: JA; row : JB; begin row.field1 := 'first'; head.fields.add(row); asm @raw = JSON.stringify(@head); end; showmessage(raw); end; The code above, when it reaches the showmessage call, produces clean JSON. No postfixing. The postfix is added to all Smart "managed" variables, but records should be excluded from that. But (!) JavaScript work by references, so seem to me you managed to fall between two chairs and when you assigned values to the data, JS produces references to the values rather than the values itself (a bit like pointers can behave under COM Delphi coding). Here is the result I got: {"fields":[{"field1":"first"}]} Now, mapping to existing data, which i presume you are doing (?), where you have a wad of JSON you load in, parse to an object structure -- and then i presume you want a ready to go array structure from that? Well, i did something similar in the memory-filesystem unit if you look there, but as a general rule i read in the data into a variant, then typecast and traverse that, copying over the data i need. Then just null the variant to get the GC to clean up. I also have a node.js websocket server that sends info as JSON on my network, and designed the structures to be simple and easy to transport. Using a typical envelope record to make identification easy, then attaching the actual message as base64 inside: type TNetworkPacket = record Identifier: string; //GUID ClientId: string; //GUID envelopeId: string; //ID of the contained type envelope: string; // Contained type, base64 encoded end; // Then i can do simple lookups for the handler when i get a package // Locate a handler for this JSON packet var handler := __dispatch[envelopeid]; // Did we support this message? Ok, dispatch if assigned(handler) then handler(Socket, Packet.Envelope):
  5. TPersistent idea

    Or just .. uses System.JSON; type TMyType = class Firstname: string; LastName: string; age: integer; end; var data := TMyType.Create; var textdata: string := ''; data.firstname :='Jon'; data.lastname :='Aasenden'; data.age := 44; // Turn object to json text asm @textdata = JSON.stringify(@data); end; // Turn JSON Text into object again asm @data = JSON.parse(@TextData); end; Javascript objects are by default serializable. But not objects that inherits from TObject. Names etc. of fields in a pure "class" without inheritance are protected from compiler changes. So you can safely do lookups etc. It compiles to a pure JS structure. Also look at classes in system.structure.pas, system.structure.json .. the structure classes provides abstraction and can do storage in most formats, including binary, with ease. The idea is to have multiple back-end formats (json, xml, binary.. whatever) that would work with the same front-end persistance
  6. RemObject JSON FileUpload

    That looks like Delphi code? Second snippet there? You have to look at the Smart classes and the RO imported classes and method-names. If you look at the generated code after importing a RODL file, you will notice: (* This codegen depends on RemObjectsSDK.js: file must be in "SmartMobileStudio\Libraries\" folder * Usage: * var channel := TROHTTPClientChannel.Create("http://localhost:8099/JSON"); * var msg := TROJSONMessage.Create(); * var service := TNewService.Create(channel, msg); * service.Sum(1, 2, * procedure(aResult: Integer) * begin * ShowMessage('Result = ' + IntToStr(aResult) ); * end, * ROErrorCallback); *) Note: in the above imported RODL file, the remote service is just called "NewService". You have to import the RODL file for your service stack. Also make sure that you have a JSON channel on your native server. You might want to look at our classes for dealing with memory and binary data, like System.Memory.Buffer. System.Memory.Allocation etc. since they will simplify working with raw data. The unit System.Types.Convert is also very important, it has a class called TDataType that allows you to encode between instrinsic types, memory and byte-arrays.
  7. requestAnimationFrame() doesnt always exist under some rendering engines, especially kiosk systems and embedded web-views. Which means it will fall back on a setTimeOut shim anyways. Things are the way they are for a reason.
  8. As for WinXP, im sorry but that is not a system we can support. Please consider upgrading your machine
  9. By the way, I spent an hour writing you yesterday, just in case you missed it:
  10. Smart bytecode compiler

    Just to make sure there is no missunderstanding: This is a bytecode assembler, disassembler and runtime implemented in Smart pascal. It is not a "smart to bytecode" compiler where you can take a smart project and generate bytecodes. Although i could probably create a bootstrap that injects your smart code into a single .exe file -- but that already exists with NodeWebkit (google it). What you get in these units is a runtime engine that uses assembly close to 68k and arm, but where variables is a known factor. So moving data between registers and variables is valid. Allocating variables on the fly is likewise allowed. The next iteration of the project is to add class and instance awareness to the virtual cpu, so that classes can be defined, members defined and instances created and managed. Then you essentially have Java but one you can shape and do whatever you like with. This is very low-level stuff and to make use of it you need a high-level parser that assembles the binary LDEF files (read: parses whatever language you have made, then uses the assembler to emit bytecodes). The cool part is that i have also implemented the same in Delphi, so it can run native there or via FPC just about anywhere. The ASM instructions are designed to be easy to port to asm.js, which is a huge topic i cant cover here -- just wanted to clear up any missunderstanding! But if you are into languages and perhaps have wanted to create your own lingo, then this demonstrates basic principles. I have avoided too much binary for the "cpu" factors to make it readable and understandable. A faster version can easily be implemented at a later date -- its a journey
  11. Did you remember to run the update program? It seems to me like you have either been extremely unfortunate and hit every possible problem in existance, or that you have some old files lingering that were not updated. Backup the RTL folder, run the update and make sure you get the fresh units. There is no way that amount of errors can occur without something being terribly wrong with the installation
  12. Black Screen of Death

    One thing that can be a bit of a challenge when coming from Delphi or FPC, is that Javascript is 100% ASYNC by nature. This means that from the time that you call the constructor, to the time that the element has been successfully created, styled and injected into the DOM - 1000 other things can happen in between. This is true for everything, even loading third party libraries, css files, images; everything is async. There is no such method in our core RTL that you describe (CreateRootElement), this is a part of Chrome itself. So unless you are using a third party library like jquery or something that doesnt synchronize with our RTL - this is too little to go on to properly help you. But when you run your program, click the buttons in the following order, check the "pause on caught exception" (note: there is a deliberate exception when the program starts checking for a feature, but this is handled so just click the blue arrow to continue). Below you see how the callstack looks for an exception i intentially created. From the event being issued, delegated, dispatched and handled. But i doubt this is where your problem stems from. I think this has to do with synchronization and that you are expecting code to execute linear. Hence your code crash because whatever you are trying to access is either not finished yet, fully loaded or in the process or being created. It is a typical thing to experience when coming from Delphi or FPC, because you are used to linear execution. Here are a few pointers on how to synchronize your code and what to override. Because there are 3 substancial differences (InitializeObject, FinalizeObject and ObjectReady. More about those as we move along). When you first look at examples etc. you will notice calls like TW3Dispatch.Execute() which is often used to create small timed delays. A bit like TTimer in Delphi but wrapped into a single method. The same class, TW3Dispatch also has other handy methods. You will find it in System.Time.pas and SmartCL.Time.pas. Smart supports partial classes, which means the content of classes can be finished and/or added to in different units (much like C#, C++ and Java has; with emphasis on Mono C#). This is very effective when building multi target frameworks. The second thing to explore is the use of callbacks when writing code that should run in a purely async environment. This can take some getting used to, but once its in your fingers you will wonder how you ever lived without it (or at least parts of it). So learning to properly synchronize things is the key here. This is why you never override the constructor in Smart, you override InitializeObject() and FinalizeObject() instead which ensures a level of synchronized execution. For example, let's say you are loading a file right? Since JS is async that means whatever Load() routine you use (even direct access to the filesystem under node.js) will finish and exit before any data is available. This can be bewildering to say the least. Until you start to make some observations on how it's best handled. Loading files can be done in many ways, but for browser apps -using the methods in TW3Storage is the easiest (in SmartCL.FileUtils.pas). Here is an example of how you could use callbacks in your own classes to better deal with async IO: // First lets define a callback. Just like events they dont need "of object" postfix // or "reference to" prefixing. Its fairly straight forward stuff. // Note: The procedure we define is actually "TStdCallback" in System.Types.pas type TMyDataClassReadyCB = procedure(Success: boolean); // OK now lets imagine we have a class for loading some text data. It takes text and builds // an object model from this. If the object model is loaded and valid then Resize() will // do layout and show the data. So let's define a loading method that takes our // callback as a parameter: procedure TMyDataClass.LoadDataFromURI(const URI: string; const CB: TMyDataClassReadyCB); begin // We let TW3Storage do the dirtywork. Note that there are several overloaded // methods for loading data in the class. This one loads any file and // returns it as text, but you can also get the data as a TStream (have a peek at the class) TW3Storage.LoadFile(URI, procedure (const FromUrl: string; const TextData: string; const Success: boolean) begin // Loaded ok? Then keep the data if Success then SetRawData(TextData); // Regardless of outcome, always call back! if assigned(CB) then CB(Success); end); end; // When calling the method from the UI, say a button-click, we can be more // flexible with how we handle control states. The callback will fire // regardless of success or failure procedure TForm1.BtnLoadDataOnClick(Sender: TObject) begin // Disable load button and listbox while we are loading. // Also set the cursor to a progress animation BtnLoadData.enabled := false; lstDataView.enabled := false; Cursor := crProgress; // Now use our loading method FMyData.LoadDataFromURI('res/defaultdata.txt', procedure (Success: boolean) begin // Loading is done! Regardless if it failed or not we adjust // the UI's state back to how it was. With one exception, // namely that we let our list remain disabled if no data is present Cursor := crDefault; BtnLoadData.enabled := true; lstDataView.enabled := Success; // Use an inline variable to remember if our list // held any data and was cleared var HadVisibleData := lstDataView.Items.Count > 0; // OK let's build a data-model if we got some data to work with. // If the load failed then clear the list. If the load failed the list // has already been disabled (see above) so we just need a clear() call. // Javascript is laid back, i prefer to be more strict. lstDataView.BeginUpdate(); try // Flush our list if it had content if HadVisibleData then lstDataView.Items.Clear(); // Any data loaded? Build an object model from it if Success then buildDataModel(); finally // Mark the list as needing a Resize() call if we either // cleared old data, loaded new data -or both! if HadVisibleData or Success then lstDataView.AddToComponentState([csResizing]); // End update, it's all good lstDataView.EndUpdate(); end; end); end; One of the root classes for visual controls, TW3TagContainer, has a method you want to investigate. It will save you a lot of time: // Does a check if the child elements are in ready-state function QueryChildrenReady: boolean; virtual; This method will recursively check that all child controls have been created and have their csReady flag set in the ComponentState value-set. I find this to be very handy when writing large and complex visual controls where you need to know 100% that children are ready before you access them. The way JS works is that even though the constructor is finished and the element created --the prototype can still be under construction in the background. It is very rare to encounter this in our new RTL, but it can occur if you expect controls to be ready after a call to Create(). There is also a method to synchronize resize calls. This is quite handy to add in the ObjectReady() method. All visual elements have an ObjectReady() method that is called after the element has been injected into the DOM. This executes just between the time when an element is finished, but before it's visually presented. If you want an immediate resize when all child controls are created and ready, calling ResizeWhenReady() is the ticket. procedure TW3MovableControl.ResizeWhenReady; Next there is type helpers. Handles for example come in many shapes, and most of them (like THandle, TControlHandle, TGradientHandle, TCSSStyleHandle) have a helper class with low-level methods. Sadly these are not always shown in the code suggestion dialog, but that will be improved on in future updates. Now TControlHandle has a method called ReadyExecute(), and as the name implies this sets a callback that fires when the element has been created and been injected into the DOM. But this is low-level so we are not talking about the whole object pascal custom-control, but rather the raw HTML element that the control manages. It should be noted that overriding ObjectReady() is the proper way to do things, but sometimes you have to "bend the rules". While a bit esoteric looking calling a method on a handle, this is perfectly valid: procedure TSomeControl.InitializeObject; begin inherited; ... Handle.ReadyExecute( procedure () begin // Do something at once when the element is injected into the DOM. end); end; I think these will help you synchronize your code when porting from Delphi or FPC. In 99% of cases where people experience problems with porting over code, it comes down to synchronization. The JavaScript Virtual Machine simply doesnt follow conventional behavior and proper use of callback handlers, events and delayed execution is the way things work. The bonus is that you are not boxed into a "fake" linear environment (which could be achieved), communicating with JS libraries and mapping directly to existing JS objects is 1:1 rather than resorting to elaborate interface files. Paradoxical as it may sound, with correct use you can easily produce code that runs faster or en-par with the tools you are used to. We have node.js code in our lab that actually out-performs native, monolithic code. When you add clustering and domain level programming to that - the possebilities are near endless. Have a peek at this demo for example: http://quartexhq.myasustor.com/applications/particles/ We use TW3Dispatch.RequestAnimationFrame() to ensure a steady 60fps rendering, but it runs fine at 120fps on relatively modest PC specs. So its a matter of going with the flow and learn the subtle differences.
  13. directive HINTS OFF issue

    Click on project options -> Compiler -> and set hints to a lower rate
  14. Save and load SQLite Database to local storage

    You can load and save to normal streams, then push that into the cache (for example). The filesystem has been there for some time, but we have deprecated it since it only comes into play with phonegap really. Uploading and catching files is done easier using standard html5 methods. But we have a baseclass and cache filesystem implementation, and will soon have node and phonegap wrapped too -- then loading and saving to real devices will be very simple. We have to finish the visual parts of SMS first before we dig into the non visual. But look at the storage units! It is very simple to base64 encode a stream and save it to the cache
  15. Async keyword

    Adding an "async keyword" for something as trivial as this seem like overkill to me, but i do see your point. There is nothing particularly useful imho and by that i mean - javascript is async by nature. It executes single threaded, with events popping in and out of existance in a manner that is hard to control. It can be controlled, but not without resorting to hacks that might look cool there and then - but will require constant maintainance for each change in the runtime engine. There are a few JS libraries that does this on github, so if you need this type of behavior then perhaps wrapping that is a better solution. There is nothing in the way of adding a "workbegins/update/ends" style mechanism per see -- but a lose "async" prefix is not on our menu. At least not in this form. What you have here can be replicated with 3 calls: beginwork (show dialog), create panels and wait until verified, close dialog and perform re-entry. We have avoided this type of coding for many reasons, first of all that users should have full control over how they wish to deal with async workload. If we forced an async closure, we would also have to compliment that with a re-entry mechanism so that code continues post return. This leads to much heavier code since a procedure must be parted. In Smart you could achieve the same with a little work, with something like: async( procedure () begin // create panels here end); So if you write a simple procedure: procedure TMyClass.Someproc; begin async(somestuff); proc2; proc3; end; The cleanest way to compile that would be: function TMyClassSomeProc() { async(somestuff, function () { proc2(); proc3(); } } This avoid a typical re-entry you would have in assembler, and just executes "the rest" of the procedure when the async finishes. Now try to debug that if you have 10-15 async's going in 3 different contexts! Which as you probably notice, is the exact same as using TW3Dispatch! Except that is cleaner and doesnt force the codegen to emit magic But ofcourse, you will have to define the re-entry yourself. Or use of the many libraries that are out there. Our goal is to provide a compatible, ad-hoc, no-nonsense pascal based system that follows suit. We could have added a ton of features, and we have been tempted, but for now at least this is not on the menu. In the future we will no doubt add other, more helpful features, like generics instead.
×