splitPath: is this also asynchronous?

Started by ubacher, June 07, 2017, 07:31:12 PM

Previous topic - Next topic

ubacher

The docu does not say that Imatch.splitPath returns a promise. So I assume that it is not asynchronous.
But it does not seem so. Not sure how to interpret the docu.

Struggling with this asynchronous stuff.

axel.hennig

In my opinion this is a very good question.

I'm also "struggling with this asynchrounous stuff". Perhaps someone familiar with JavaScript (and similar languages) can explain this to us.

I would be interested in things like:


codeblock1{}
codeblock2{}
codeblock3{}


When/where/how do I see if codeblock1{} is executed (and finished) before codeblock2{} and this one before codeblock3{}. And when/where/how do I see if every codeblockx{} is executed at the same time?

And how can I make things like (and be sure that both lines are not executed at the same time):


{A: run this code at the beginning}

{B: start running this code after A has finished}


Mario

Quote from: ubacher on June 07, 2017, 07:31:12 PM
The docu does not say that Imatch.splitPath returns a promise. So I assume that it is not asynchronous.
But it does not seem so. Not sure how to interpret the docu.

Struggling with this asynchronous stuff.
Yes, asynchronous. Returns a promise. I will update the doc for the next release.

Tip: You can always look at the source code of the methods. There is a reason why I include imatchlib.js in source code.
Then you see it makes a get request. This way it can call the same function IMatch uses internally for optimal compatibility.

If all you want is to splt a file name / string. you can use the JavaScript split function https://www.w3schools.com/jsref/jsref_split.asp or https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Mario

Quote from: axel.hennig on June 07, 2017, 08:07:39 PM
I'm also "struggling with this asynchrounous stuff". Perhaps someone familiar with JavaScript (and similar languages) can explain this to us.
I would be interested in things like:


codeblock1{}
codeblock2{}
codeblock3{}


This code is executed in exactly this order.
If your code blocks are in  a <script> tag they will be executed from top to bottom, in the order in which they appear in the source code.

Or do your code blocks make asynchronous calls (calling into IMWS)?
In that case, all code blocks will executed at the same time (!) and may take different times to complete.
Give us a more detailed example.

If you do IMWS.get(...) or IMWS.post() it returns a promise and returns immediately. You code then continues. If you want to run code when the promise is "done", either successfully, with an error or in either case, you need to provide callback methods via then(), error() or always().

See the dedicated section on Promises in the IMatch Developer Center Code Recipes section and the many tutorials out there which explain about this important JavaScript concept. It takes a bit to wrap your head around this, or to understand that when you do AJAX callls (IMWS.get()) that multiple parts of your program may run at the same time, and be finished at different times.



-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

thrinn

Quote from: ubacher on June 07, 2017, 07:31:12 PM
The docu does not say that Imatch.splitPath returns a promise. So I assume that it is not asynchronous.
When you look into imatchlib.js you will see that splitPath resolves to a function which calls self.get(). The get in turn is only a wrapper for a jQuery AJAX call (I am sure Mario mentioned this somewhere). And AJAX calls are asynchronous.
Conclusion: splitPath is also asynchronous.

Edit: Mario has been faster, as always. :-)
Thorsten
Win 10 / 64, IMatch 2018, IMA

Mario

-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

ubacher

I tried a relatively simple task:
Take the selected files, assign the first file a 1 star rating, assign all files a category and move them to a sub-folder.
In the simplest form this results in code nested 8 deep - difficult to keep track of.

So I am starting to look at this asynchronous programming like handling "interrupts" - for those of you who have done some
programming close to hardware this will be familiar.

Here what I will try next:
A MASTER function looks after the sequencing.
It posts all requests. Each asynchronous request, as it completes, calls the MASTER.
(I look at the MASTER as the "interrupt handler".)

This may fail because it in effect has two functions calling each other. Not sure if javascript allows this.
And then there is the matter of scope which needs to be properly handled.

I will report back.

PS: When we have a script which puts up a window with buttons to click then it is easier to understand this asynchronous stuff:
As a button is clicked it posts an asynchronous call.
As each asynchronous call completes it just updates what is shown in the window. This may result in the buttons being changed.
Maybe I will write my script first with one button whose functionality will change - allowing me to step thru the program.
After that I can eliminate the button.

(just got warned that there is a newer post -- posting anyway.)

Mario

jQuery's when() method allows you to wait for the completion (or failure) of multiple promises:

https://api.jquery.com/jquery.when/

8 levels deep is way to complex. I don't see why you would need such a complicated thing.

You assign the rating.
You assign all files to categories.
You move them.

3 things.
You can do them one after the other simply by waiting for the previous operation to complete.
Or you do them all at once. Just be careful not to move files while changing metadata. Because if a user has enabled background write-back, IMatch may be writing to files (after changing the rating) while your script is trying to move these files to another folder. Your script is responsible to deal with that.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

ubacher

Get the selected files:
  will have to wait for this

Assigning categories and rating
- wont have to wait for this (Assuming it will work even if the files are moved)
   But: if the assigns fail for whatever reason I may want to abort what follows.
          In this case I DO have to wait for completion. See questions below.

Split the file name to get the folder using Imatch.SplitPath 
   Must, of course, wait for this (because of this it does not make sense using this function - use plain code to split)

Create a new folder
    I have to wait for this to complete before I move the files

Move the files - has to be done one by one.
     For robust programming I will want to check the folder of each file first to make sure all are from the same folder.
     No problem if I don't use the Imatch.Splitpath.
    Since this is the last step I would not have to wait but:
        I need to report any errors in case they occur.

And this brings up another questions:
        If any of the asynch posts fails and I haven't waited for them - how do I report the errors?
and   If I already closed the app and some calls have not completed - and now complete with error - what happens?

     

ubacher

#9
Follow up request:
Could we have a sample code for the jquery when functionality.

(My thinking: should probably use this before exiting an app.)

And the following just occurred to me:
If we were to place a wait for completion code after each asynchronous call then we could program
just like in a serial code environment. Would solve a lot of headaches - get us going faster.
If the execution turns out objectionably slower we can rethink the approach.

Mario: Is there some relatively universal code you could provide us for this?

ubacher

Another request/suggestion:
Mario: Could you provide us with a ready made function to replace the splitPath.
"splitFileNameIntoComponents"

Mario

QuoteIf any of the asynch posts fails and I haven't waited for them - how do I report the errors?

That's why jQuery allows you to handle errors in asynchronous requests by supplying a function that is called when the server returns a HTTP error message. You also need to check the result returned by IMatch, it may also indicate an error.


QuoteIf I already closed the app and some calls have not completed

Closing the app closes the integrated browser and this stops JavaScript as well. Your pending requests will be canceled.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Mario

QuoteCould we have a sample code for the jquery when functionality.

There are literally thousands of tutorials, blog entries and YouTube videos explaining how to work with jQuery AJAX calls. And there is of course the jQuery documentation which not only explains about when but also shows many examples. Did you look there?

QuoteIf we were to place a wait for completion code after each asynchronous call then we could program
just like in a serial code environment.

This is what when() is for. It allows you to bind a function when all your asynchronous calls are done. But writing an app that performs multiple parallel web requests is adding unneeded complexity - I would not got that road until I absolute have to.

So far nothing of what you described above requires your script to run multiple requests at the same time. Also, remember: While your multiple requests are running, the IMatch user may interfere, delete a file, switch to another folder, close IMatch etc.

You get the idlist.
when this is done you iterate over the files and call whatever routine you need to move the file. This routine then calls itself (in then()...) with the next file, or stops when all files have been processed. The "ProcessFiles" sample script shows show to do this, even with keeping a progress bar updated and an option for the user to stop the process.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Mario

Quote from: ubacher on June 09, 2017, 01:07:11 PM
Another request/suggestion:
Mario: Could you provide us with a ready made function to replace the splitPath.
"splitFileNameIntoComponents"
why? If you need that you can implement that quickly yourself. The idea of the built-in splitPath is that it uses the same routines as IMatch, this properly handling regular paths, network paths and UNC shares.

https://www.google.com/#q=javascript+splt+windows+filename
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

ubacher

QuoteBut writing an app that performs multiple parallel web requests is adding unneeded complexity - I would not got that road until I absolute have to.
It is exactly that I want to avoid multiple request. Waiting after each request until it returns a promise and only then continuing.

Makes all code a serial code.

Mario

No, it does not.

If you "start" multiple requests, they are all processed in parallel and may take different times to complete. You cannot move your files before you know their file names, right? This is why you need to wait for the "get seleted files" and then start your move.

when() allows you to call a function when two or more asynchronous calls have completed. It does not make them sequential or anything.

You need:

1. Get selected files....then(
2. For each file
    fileindex = 0;
    call MoveRoutine


function moveRoute
   prepare path, whatever.
   moveFile... then(
     fileindex++;
     if fileindex < number of files then
        moveRoutine
        return,
     end
end

This makes your code sequential.
You get the files to work with
you call the moveRoutine function which processes the next file.
This routine calls itself as long as there are files to process.

See the Process File Sample script, which does this all.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

ubacher

#16
I understand this. And it results in deeply nested scripts.
I was hoping that when() would let me wait after the posting of a script
until it is fulfilled.

I just read about when() and it does not wait but executes yet another function upon completion.
So this will not work for what I want to do.

I suppose one could use  a boolean and put  a loop like this after the posting of the request :

while (promisenotyetfullfilled) delay 10 ms  !

where promisenotyetfullfilled would be set when the promise completes.

Mario

#17
This is exactly not what you want to do. Looping, burning CPU cycles while you want for a web service request. Which can take 0.1 or 5 seconds. Not good.
Maybe the following example gives you some ideas:



This calls A. When A is completed it calls B. When B is completed it calls C.

A and B just return the promise. This means we can chain the function calls via then. "data" is whatever the web service call returned.
Best to copy/paste it into a HTML file, set breakpoints into each function in your debugger and then let it run.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Carlo Didier

Quote from: Mario on June 09, 2017, 06:26:02 PM
This is exactly not what you want to do. Looping, burning CPU cycles while you want for a web service request. Which can take 0.1 or 5 seconds. Not good.
Maybe the following example gives you some ideas:



This calls A. When A is completed it calls B. When B is completed it calls C.

A and B just return the promise. This means we can chain the function calls via then. "data" is whatever the web service call returned.
Best to copy/paste it into a HTML file, set breakpoints into each function in your debugger and then let it run.

But there's exactly the problem. As ubacher says, you'll end up with deep nesting.
A().then(B).then(C); is simple, but at some point you'll end up with A().then(B).then(C).then(D)..........then(Z)!

And if you want it readable, you won't name your functions A, B, C, etc but with meaningful names. Imagine the mess.

For linear programming (which may well be required in some cases, or just for simplicity!), you would need an option to wait for completion, like some other scripting languages offer.
For example calling a function with an optional parameter MyFunction(standard parameters,optional boolean wait) and the function simply returning the result or an error.

Mario

AJAX is asynchronous by design. This is how it has been implemented in JavaScript.
This is not my design, or an IMWS specialty or something.

It's the core of JavaScript and all modern programming languages. Being responsive, doing several things at the same time to utilize all processor cores available even on smart phones today is a key technique.

There is no "wait until this AJAX call is done" way of doing this, if you are looking for that. There once was, but this has been dropped quickly as a bad idea.

Again, your script does not need to do anything special. Your browser will call the callback function you provide when the request is done.
Then you can continue with whatever you need to do. Until then, your script does not do anything.
This is not the same as blocking, because your script may need to update the user interface, react on a button press or something. Or maybe it processes a batch of data already received previously.

Your thinking model currently is:

1. A
2. B
3. C

This works fine if none of the tasks is asynchronous. A is done, then B and C. In that sequence. Nothing runs in parallel, and your script blocks as long as A, B and C are running. No screen updates etc. If A takes 10 seconds to complete, you have a problem.

If A is asynchronous and takes 10 seconds, B and C will run while A is still running. This is a good thing because it allows your app to do several things at the same time.
If you can run B only when A is done, and C only when B is done, you need to change your logic.

if A ís done then
if B is done then
   C
  end
end


Also not unusual. NOTE: You don't need to write your code "in" the function you supply to then. You can also just call a regular function somewhere else in your code.

In all example scripts shipping with IMatch, including rather complex ones like the Map Panel, I never had any problems with deep nesting or asynchronous functions more than two or three levels deep. And 3 levels is used only for the initial setup of the app, where the app needs the user info, the database info some previously stored user settings and then also has to translate the whole app.

If you want to write code which waits on multiple promises before continuing and you don't want to call code from then() use when() to wait until all your pending requests are done. But that is rarely needed.

Or, wrap everything you need to do into a function, and let this function return a promise. I demonstrated that in several of the sample apps. Search for jQuery.Deferred() This allows you to bundle multiple operations in a function and when all is done, a callback function is executed which then does whatever you need to do.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

sinus

Quote from: Mario on June 12, 2017, 11:34:25 AM
AJAX is asynchronous by design. This is how it has been implemented in JavaScript.
This is not my design, or an IMWS specialty or something.

It's the core of JavaScript and all modern programming languages. Being responsive, doing several things at the same time to utilize all processor cores available even on smart phones today is a key technique.

There is no "wait until this AJAX call is done" way of doing this, if you are looking for that. There once was, but this has been dropped quickly as a bad idea.

Again, your script does not need to do anything special. Your browser will call the callback function you provide when the request is done.
Then you can continue with whatever you need to do. Until then, your script does not do anything.
This is not the same as blocking, because your script may need to update the user interface, react on a button press or something. Or maybe it processes a batch of data already received previously.

Your thinking model currently is:

1. A
2. B
3. C

This works fine if none of the tasks is asynchronous. A is done, then B and C. In that sequence. Nothing runs in parallel, and your script blocks as long as A, B and C are running. No screen updates etc. If A takes 10 seconds to complete, you have a problem.

If A is asynchronous and takes 10 seconds, B and C will run while A is still running. This is a good thing because it allows your app to do several things at the same time.
If you can run B only when A is done, and C only when B is done, you need to change your logic.

if A ís done then
if B is done then
   C
  end
end


Also not unusual.

This is an explanation, that even I understand it.  8) Thanks for it.
And thanks all others to ask so good things. :D
Best wishes from Switzerland! :-)
Markus

ubacher

Returning to splitPath:

Here the javascript code (fragment) which seems to work:
var path = '';
                   
console.log (response.files[0].fileName);  // as returned  by a promise
path = response.files[0].fileName;
path = path.substring(0,path.lastIndexOf("\\")+1);  // this oneliner does it all
console.log (path);