14

Note: The issue with delay was fixed in OS X 10.11 El Capitan.

Ever since I upgraded to Yosemite, Applescripts that use delays have stopped working. How can I fix this?

Here's the world's simplest Applescript, for the sake of an easy example:

set volume output volume 0
delay 5
set volume output volume 20
delay 5
set volume output volume 0
delay 5
set volume output volume 20
delay 5
set volume output volume 0
delay 5
set volume output volume 20
delay 5
set volume output volume 0

That should take 30 seconds to complete. If I run it in Script Editor (formerly Applescript Editor) it takes 30 seconds to complete. But if I save this script as an app, when I launch the app, the delays are ignored and the app takes a fraction of a second to complete.

How can I force Applescript to delay for a specified amount of time before moving on to the next step? Is this a Yosemite glitch? Is there a reliable workaround?

6
  • This works as expected on my Mac (10.10.1)
    – markhunte
    Dec 14, 2014 at 10:21
  • Any idea what causes it not to work on mine? To clarify: it works in Script Editor, but if I save the script as an app and then launch the app, the delays are ignored. It's the strangest thing.
    – 2oh1
    Dec 14, 2014 at 18:09
  • I have the same problem on 10.10.3 Jun 29, 2015 at 9:49
  • Reported to Apple: openradar.me/21588747 Jun 29, 2015 at 10:05
  • 1
    This issue was fixed in OS X 10.11 El Capitan.
    – Chris Page
    Dec 9, 2015 at 2:59

7 Answers 7

8

Note: The issue with delay was fixed in OS X 10.11 El Capitan.

@2oh1, you have the right basic idea in your answer, but here's a complete and correct answer:

The only reasonable way to work around this is to invoke "delay" within a loop that ensures the desired duration elapses before continuing. The best way to do this is to override "delay" with a custom handler:

on delay duration
  set endTime to (current date) + duration
  repeat while (current date) is less than endTime
    tell AppleScript to delay endTime - (current date)
  end repeat
end delay

This enables you to leave the rest of your script unchanged and you can use "delay" normally, e.g.,

delay 5
display alert "I like cake!"

[NOTE: Normally, the custom handler would use "continue delay duration" to invoke the built-in "delay", but I found that, although this works within Script Editor, it returns an error when used within an applet (“Can’t continue delay. (-1708)”). I worked around that issue by directly telling AppleScript to handle the delay command instead of using "continue" to get there.]

The issue is that delay processes user input while pausing the script, so you can still click on menus or windows displayed by an applet, and there is a bug (fixed in 10.11) where user input causes delay to not wait the full duration before resuming script execution. If you don't interact with the applet, delay works correctly.

7
  • Is all of this a bug, or is there a reason delay is working this way with Yosemite?
    – 2oh1
    Feb 25, 2015 at 9:28
  • It is a bug that delay isn't delaying.
    – Chris Page
    Feb 25, 2015 at 22:30
  • It's so strange. I was tinkering with an applescript that uses delay the other night, and suddenly, delay was functioning correctly again. I assumed the bug had been fixed. An hour later, even though nothing had changed, the bug was back. I'm truly baffled. It doesn't matter though. I'm using a variable to set the time and delay until five minutes later (for example), and it's working perfectly. So... problem solved, but that doesn't explain why the problem exists. Interesting, interesting, interesting.
    – 2oh1
    Mar 15, 2015 at 0:14
  • 1
    Because Apple goofed. Apple software is much less reliable since they started releasing OS X yearly. I miss the higher OS X minor versions (like 10.6.8) where the OS was rock-solid. You just don't get that kind of experience anymore. Jun 9, 2015 at 7:08
  • If it's a bug (which I agree with), why isn't this still not fixed, I wonder. Has anyone here made a radar report? Would you then please post its ID? (or/also post on openradar.me) Jun 29, 2015 at 9:53
6

While battling this same problem I came across this answer to a not-so-related question and decided to try it and it seems to work for me.

Replace delay 5 with do shell script "/bin/sleep 5" and get the same result.

3
  • Thanks for sharing! As I said earlier above, I'm unable to test this because, for reasons that make no sense to me, delay 5 is working as it should for me right now (I just tested it again a moment ago). I have no idea why this sometimes works and sometimes fails. It's so strange. I ended up using a workaround where I get the current time and set it as a variable, and then pause the script until the time is variable plus one minute (for a 1 minute delay, obviously). It's clunky, but it's worked flawlessly for months, whereas a simple delay has been intermittent. Ugh.
    – 2oh1
    Jul 28, 2015 at 0:59
  • I am running 10.10.5 and this solution does not work and also locks the script for the duration of the sleep
    – Greg
    Dec 8, 2015 at 7:39
  • @Greg: If it “locks the script” for the sleep duration, then it's working. /bin/sleep waits for the sleep duration and does not return until it has completed. In contrast, AppleScript's built-in delay command handles user input events while the script is paused. (Which is where the bug lies: if there is user keyboard input, delay continues script execution before the delay duration has completed.)
    – Chris Page
    Dec 8, 2015 at 21:04
3

I'm not saying this is the best solution, but it seems to have solved my problem. Instead of using a simple delay, which is being ignored for reasons I do not understand, I've switched to getting the time and looping until a new time is reached (it still uses a delay, but it doesn't matter if it ignores the delay since the script doesn't continue until the time is reached).

# Pause for five minutes
set GetTheTime to current date
set NewTime to GetTheTime + (5 * minutes)
repeat while (current date) is less than NewTime
    delay 60
end repeat

I'm still dying to know why delay is being ignored (or dramatically sped up?!??), but this gets the job done, clumsy as it is.

3
  • The "delay" command isn't being "sped up", it's being interrupted. While delaying, it sits in a loop checking whether there are any keyboard input events so it can check for Command-Period. Apparently it is incorrectly exiting the delay loop when any user input is detected.
    – Chris Page
    Feb 25, 2015 at 6:18
  • Interesting! I'm finding that it's being ignored (sped up? interrupted?) even when my Mac is sitting idle, but I don't know why.
    – 2oh1
    Mar 15, 2015 at 0:07
  • Once a user input event is in the event queue, delay will be continue to be interrupted until something handles the events and removes them from the queue.
    – Chris Page
    Dec 10, 2015 at 11:35
2

I found a work-around in a German forum post. Add these lines to the top of your script:

use framework "Foundation"
use scripting additions
current application's NSThread's sleepForTimeInterval:1
5
  • I'm having trouble testing your answer because, for whatever reason, delay is currently working as expected on my Mac. I have no idea why. I'm running 10.10.3. Perhaps Apple fixed this bug? Or, maybe it's just an intermittent problem that isn't effecting my Mac at this moment. Ugh. As I said above though, my workaround is having Applescript get the current time and then delay until current time plus x.
    – 2oh1
    Jun 29, 2015 at 17:57
  • I am running 10.10.3 as well and do still see the issue. Since not everyone sees the issue, it's probably intermittend or related to 3rd party software or to certain preference settings. Who knows. BTW, your work-around has probably the disadvantage that the app keeps using 100% CPU time while waiting, whereas the proper delay behavior is to put the app to sleep for that duration, thereby using less power. Jun 30, 2015 at 18:49
  • I am running 10.10.5 and this solution does not work.
    – Greg
    Dec 8, 2015 at 7:39
  • @ThomasTempelmann: The issue occurs when there is user input. If you don't click or type while the script is running, the bug isn't triggered.
    – Chris Page
    Dec 8, 2015 at 21:00
  • Note that this solution causes the application to actually freeze for the duration of the sleep. delay processes user input while the script is paused, so you can still interact with menus and windows, for example.
    – Chris Page
    Dec 8, 2015 at 21:23
0

carzyj had what I consider the best answer, but when combined with Chris Page's method, you get:

on delay duration
  do shell script "/bin/sleep " & duration
end delay

You can comment out "on delay" through "end delay" to revert to the original delay.

1
  • I also made a similar code modification, before seeing this. I am running 10.10.5 and this solution does not work and also locks the script for the duration of the sleep.
    – Greg
    Dec 8, 2015 at 7:41
0

this is a modification to @chris-page solution

It seems to be a balance between responsiveness and accurately capturing the delay.

on delay duration
    set endTime to (current date) + duration
    repeat while (current date) is less than endTime
        set delta to duration / 100
        if duration < 0.2 then
            set delta to 0.2
        end if
        tell AppleScript to delay delta
    end repeat
end delay

But instead of telling Applescript to delay for the overall duration, we just tell it to delay for a fractional period of the duration. if the period is less than what apple allows (1/60 of a second), then let's just set it to that delta. That we can keep some responsiveness, yet still be accurate. the suspicion is that sometimes delay doesn't work so the repeat while loop will keep the thread locked up, but in success scenarios, we want the delay delta to be short so that the process can still be interrupted

2
  • Shortening the requested delay is unnecessary and can only make the script run slower and consume more CPU while waiting. My version merely calls delay until the entire desired duration has elapsed, allowing delay to delay as much as it can before it continues execution.
    – Chris Page
    Dec 8, 2015 at 21:22
  • Note that since this answer was posted, I updated my code to use delay endTime - (current date) instead of delay duration, which ensures that if delay doesn't get interrupted it won't pause the script any longer than the originally requested time, which may have been what you were trying to address with this answer.
    – Chris Page
    Dec 9, 2015 at 3:02
0

So, to put it all together, I believe Chris means this:

on delay duration
   set endTime to (current date) + duration
   repeat while (current date) is less than endTime
      tell AppleScript to delay endTime - (current date)
   end repeat
end delay

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .