Jump to content
phallstrom

Anyway to delay script filter from running (ie. wait until user has stopped typing or at least paused)

Recommended Posts

I'm playing around with workflows and while working on one happened to need to look up an IP address.  So started working on a workflow that would let me type things like "dig alfredapp.com" and "dig alfredapp.com mx" and get the results back.

 

It's easy to do if I use notifications or large text.  But I wanted to cheat and keep it inline.  Like the below.  Which works, but it fires off the script at "xxx a" and "xxx al" and "xxx alf", etc.  What I'd love is a checkbox that said "don't run script until user hasn't typed for 100 milliseconds".  I know I could do it by not executing the long part of the script until the last character was say a '.' or something, but I was hoping we could get a delay-for feature?

 

And I know I'm already abusing the script filter stuff, but it looks cool and I noticed some other workflows (weather) do it as well.

 

Any advice?

 

Screen%20Shot%202013-03-15%20at%208.03.4

Share this post


Link to post

...I was hoping we could get a delay-for feature?

 

Everyone's been asking for this. There's no clean way to simulate it in a script either, since there's no state stored across invocations for the same Alfred window. Until it gets added the best hacks are going off of length or other trigger. In your above example you might wait until you see a valid ending, e.g., 'mx'.

Share this post


Link to post

You could use this function, couldn't you? http://php.net/manual/pt_BR/function.sleep.php

 

This works fine, not the cleanest way, but meanwhile...

 

Everyone's been asking for this. There's no clean way to simulate it in a script either, since there's no state stored across invocations for the same Alfred window. Until it gets added the best hacks are going off of length or other trigger. In your above example you might wait until you see a valid ending, e.g., 'mx'.

 

The right thing is not to do the wait in the script, but before running. Something like a timer, cancelled and delayed 100ms more on every keyboard type.

Share this post


Link to post

Sleeping within my script won't work because by then it's too late.  The issue is that with a script filter the script is executed after every keystroke.  I want alfred to delay script execution for 100ms (or so) after the last keystroke has been pressed.

 

Normally this isn't an issue.  The script filter is just there to help narrow your options which will then run the actual script.  However, I'm abusing it a bit (like the weather workflow) to show actual results.  If you have the weather workflow installed you can see what I mean by typing "weather 9" and then waiting for it to load results.  Now hit "8" and it refreshes again.  Every keystroke triggers the script execution which in my case (dns lookups) can sometimes take a little while.  I'd prefer to delay it until the user has clearly paused.

 

I'll figure out a work around for now, good to know people have asked about this already.

Share this post


Link to post

You could always try using a regular expression to match the full input string. Make sure that a full url or ip, plus the ending "mx". That way, it begins to execute the script but sees that the input isn't complete so it doesn't actually do anything yet. Once it sees that the input matches the regular expression and therefore the entire input is there, then execute and produce XML

 

Just a thought..

Share this post


Link to post

Yeah, that won't really work since "dig david" might be perfectly valid and resolve.  I could make you put a '.' on the end.  I think what I'm going to do is wait for a space at the end since that will never show up for a hostname.  Then I'll do an A lookup (common case) as well as provide filters for the other DNS types (mx, cname, txt, etc.).  So then I can either run my app with a space at the end or if the last argument is one of the dns types.

 

Should work okay.  

 

Then I suppose I should go update my earlier extensions :)  Not financially rewarding, but it sure is fun :)

Share this post


Link to post

@David - Do you know what happens in a script filter when typing quickly?  Does alfred kill the currently running script before starting the next one?  Or?  I'll see if I can figure out by watching for traps, but if you know, please share. Thx!

 

EDIT: Looks like each script gets run in it's own thread (or similar) and isn't killed.

Edited by phallstrom

Share this post


Link to post

I'm also interested in this feature.

You can't always use a regex to keep things under control, as sometimes the user input doesn't have any special meaning that can be roofed under a regular expression.

 

I don't have a good example(s) for script(s) that might use this feature apart from mine at least, but I'm sure people on here are creative enough and will find a use to it quickly enough :).

 

TL;DR Please add this feature as a checkbox under the settings of a 'Script Filter' in a workflow. I'm sure it'll be useful for enough workflows.

Edited by The_Ben

Share this post


Link to post

Yeah, that won't really work since "dig david" might be perfectly valid and resolve.  I could make you put a '.' on the end.  I think what I'm going to do is wait for a space at the end since that will never show up for a hostname.  Then I'll do an A lookup (common case) as well as provide filters for the other DNS types (mx, cname, txt, etc.).  So then I can either run my app with a space at the end or if the last argument is one of the dns types.

 

Should work okay.  

 

Then I suppose I should go update my earlier extensions :)  Not financially rewarding, but it sure is fun :)

 

You could definitely do what you are saying. Create multiple script filters that all have the same keyword. Have them titled appropriately for what you want to do and then just pass the argument to it and make it not fire until there is a space or some delimiter as the final character so it knows its safe to run.

Share this post


Link to post

Hey guys,

I just ran into this problem myself... I thought of writing down the microtime() into a plist or a text file at the beginning of the execution, wait for a bit, and check if the number written down has changed. If yes, then I don't do anything because there is a more recent instance of the program running, if not, then I actually do run it.

But it's not working.

Any new ideas in there?

Share this post


Link to post

Hey guys,

I just ran into this problem myself... I thought of writing down the microtime() into a plist or a text file at the beginning of the execution, wait for a bit, and check if the number written down has changed. If yes, then I don't do anything because there is a more recent instance of the program running, if not, then I actually do run it.

But it's not working.

Any new ideas in there?

 

Is there any way that you could parse the input text and only fire the script if it matches a certain pattern? For instance, using some kind of regular expression to check the input text to determine if its ok to run the script?

Share this post


Link to post

Not really... All I can do is setup a minimum length for the query.

Do you know what makes a new instance of a script fire? Waits for the last active one to finish? Triggers at every change in the query? Or what else?

Share this post


Link to post

Not really... All I can do is setup a minimum length for the query.

Do you know what makes a new instance of a script fire? Waits for the last active one to finish? Triggers at every change in the query? Or what else?

 

Forcing one to run isn't possible. I will note though that, according to Andrew, script filters don't necessarily execute on EVERY keystroke. If the previous run hasn't returned you can keep typing and it will execute again when it catches up. So, his example was.. If I typed 'keyword a' and before it finished running the script, I typed 'ndrew', it wouldn't go back and execute the script for every letter. It would run for 'a' as the input, and then again for 'andrew' as the input.

Share this post


Link to post

So it never runs 2 threads at once? Then it sounds impossible to accomplish this functionality (only run the "real" one when the user has stopped typing). But I remember debugging a script filter once and seeing multiple instances of it running at once...

 

I don't really need to force one to run. But if there are several running at the same time, it's easy to determine which is the most recent and abort all the other ones, thus making the whole thing smoother and faster.

 

I don't really think it makes sense to wait for a script to return if there is already another query typed in, no?

Edited by Florian

Share this post


Link to post

So it never runs 2 threads at once? Then it sounds impossible to accomplish this functionality (only run the "real" one when the user has stopped typing). But I remember debugging a script filter once and seeing multiple instances of it running at once...

 

I don't really need to force one to run. But if there are several running at the same time, it's easy to determine which is the most recent and abort all the other ones, thus making the whole thing smoother and faster.

 

I don't really think it makes sense to wait for a script to return if there is already another query typed in, no?

 

The only robust solution to this (until/unless support is directly added to Alfred) is to kick off a daemon process that does the work and returns results. Then your script communicates with the daemon and returns immediately if no results are available. In the daemon you can throttle the lookups. Of course, there's the issue that Alfred waits for child processes kicked off by scripts so your daemon will have to work around that. :-)

Share this post


Link to post

Arf... Sounds like the good way to go but I'm so lazy! Is there any code I could steal from somewhere from someone who's done that already?

 

But seriously, it sounds to me like a major thing to add to Alfred because all the internet related workflows would have a much better feel to them with that.

Share this post


Link to post

I'm feeling this pain, too, at the moment. I've got a web service that I query and the results can take 2-3 seconds to return. Since Alfred doesn't kill existing Script Filter scripts as the text "changes," my results are always a little delayed in getting to me (leaving the original, incorrect results in place during that time).

 

It's been about a year since anyone posted here; is this on the roadmap at all?

Share this post


Link to post

Andrew has been very mysterious about the future of alfred. Any feature request or debate is met by a "I have some idea for the future of alfred" or something alike.

 

I have turned to using separate daemon processes. Php has an integrated server (php -S localhost:8888), node too, and i'm assuming it's doable with all languages. 

Share this post


Link to post

Yeah, for now, you still have to fake this.

 

To elaborate a bit on what Florian has said above: basically, Alfred will wait for each process to finish before getting new ones, so you end up waiting for those processes to finish even though you will never use them. The best solution, so far, is to have your script filter kill off all running processes of itself except for the current one.

 

So, basically, have the script filter get its pid, and then have it grab all the processes running with a script of its same name, then continue with the logic.

Share this post


Link to post

I'm not following this. If like Aaron B. you have a query that takes 2-3 seconds to return, how does using a background process help?

 

As soon as your Script Filter exits, it no longer has a way to return results to Alfred, so if it hands off to a background process and exits, how are the results returned to Alfred? Or is this only for non-Script Filter applications?

 

How would a Script Filter process kill its "siblings"? Alfred will only run one at a time, so there can't be any siblings unless Alfred isn't working properly.

Share this post


Link to post

Its a hack, but for my internet service workflow (LibGen), I just require a `.` at the end of the query. So, the filter won't run the web request and scraping code until it gets a query that ends with a period. This means the initial filter calls die basically immediately (this check is the first code that's run when the query comes in). I then just have an informational, non-valid result that is returned while the query is lacking the ending period. It looks like this:

 

Screenshot%202014-12-18%2011.31.09.png?d

Not the most elegant, but it helps a lot with speed and responsiveness.

Share this post


Link to post

Personally I'd just start a background worker and push tasks to that. And make the worker always process and return the newest result.

 

It depends on the language you're using though, with PHP that might be a bit of a pain to get working ;)

Share this post


Link to post

Andrew has been very mysterious about the future of alfred. Any feature request or debate is met by a "I have some idea for the future of alfred" or something alike.

 

I have turned to using separate daemon processes. Php has an integrated server (php -S localhost:8888), node too, and i'm assuming it's doable with all languages.

 

Very interesting. Do you have some convenient way for users of your workflow to spin up a background worker? Or is this applicable to your own, non-shared workflows?

 

Its a hack, but for my internet service workflow (LibGen), I just require a `.` at the end of the query. So, the filter won't run the web request and scraping code until it gets a query that ends with a period. This means the initial filter calls die basically immediately (this check is the first code that's run when the query comes in). I then just have an informational, non-valid result that is returned while the query is lacking the ending period. It looks like this:

 

Screenshot%202014-12-18%2011.31.09.png?d

Not the most elegant, but it helps a lot with speed and responsiveness.

Not a bad workaround at all; just wish we didn't have to resort to that. :(

 

Personally I'd just start a background worker and push tasks to that. And make the worker always process and return the newest result.

 

It depends on the language you're using though, with PHP that might be a bit of a pain to get working  ;)

Same question as I asked Florian: do you have some way for this background worker to be created when a random user uses your workflow, or is that workflow something internal to you (where you can guarantee that the worker is running)?

Share this post


Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...