15

Javascript's setTimeout function is a method of the window object. This object doesn't exist in ExtendScript and is therefore not available to scripts made for Adobe InDesign or Illustrator. What can I use instead to acheive the same results?

Shawn
  • 10,931
  • 18
  • 81
  • 126

3 Answers3

21

It's part of the extendscript's $ object.

$.sleep(1000) //tell extendscript to sleep 1000 milliseconds

Not the same as setTimeout() but you should be able to make it work for you.

EDIT: Here is setTimeout extension for extendscript:

$.setTimeout = function(func, time) {
        $.sleep(time);
        func();
};

$.setTimeout(function () {alert("hello world")}, 3000);
pdizz
  • 4,100
  • 4
  • 28
  • 42
  • 1
    +1, but actually, my context requires the ability to cancel the timeout. Basically, I am launching an external application and pooling a certain log file to detect that application's termination. 10 seconds after launch, I declare the operation "timed out", presuming that something failed in the external application. So I need something like `var t = setTimeout(stopPooling, 10000)`. If by pooling the log I can determine that the external application terminated successfully (under 10 seconds), then I need to cancel the timeout (`clearTimeout(t)`) so as not to call `stopPooling` unnecessarily. – Shawn May 22 '12 at 18:25
  • If you can test the success of running the external application couldn't you modify `func();` to `if(!appRan) func();`? I don't know how to approach writing clearTimeout() in extendscript. – pdizz May 22 '12 at 18:34
  • 1
    I suppose so.. But I have two different things to time. I want to pool my log file every 0.1 seconds, and also timeout after 10 seconds. Something like `var t, i; i=setInterval(function() { if(appRan) { clearInterval(i); clearTimeout(t); handleAppSuccess(); }}, 100); t=setTimeout(function() { clearInterval(i); handleAppFailed(); }, 10000);` I guess I could do this with `$.sleep` using a counter: `for(c=0; c<100; ++c){ $.sleep(100); if(appRan){ success=true; c=101; }} if(success){ handleAppSuccess(); } else { handleAppFailed(); }`, but other situations may require a real non-blocking method... – Shawn May 23 '12 at 19:49
  • this does also not unwind the stack / push the task at the end of the event loop. – R D Oct 11 '17 at 07:47
  • 1
    this is useful to wait for async actions like `app.executeCommand` to finish – ktx May 05 '18 at 14:08
  • THIS IS WORKING THANK YOU. – Aljohn Yamaro Oct 27 '19 at 16:25
  • This is very different from setTimeout: with setTimeout, the execution goes on in parallel. With sleep, the execution is halted. There are very few proper uses for sleep. Also note that, in InDesign, the display won’t refresh during sleep. – Philippe-André Lorin Nov 24 '19 at 09:21
4

There is a setTimeout ExtendScript implementation here (German site, with code commented in english), but since it relies on app.idleTasks, it works in InDesign only (no Photoshop).

I paste here the downloadable code found in the link above (© Andreas Imhof):

/**
 * setTimeout
 * Version 1.0
 * A setTimeout function implementation for InDesign ExtendScript like known from a Browser's Javascript.
 * Uses InDesign's idleTask stuff.
 * Timeout milliseconds are not accurate, but it allows to call a heavy load script,
 * split it up into small junks for InDesign is not blocked too long and has time to breath.
 *
 * The script MUST run in its dedicated target engine:
 * #target "InDesign"
 * #targetengine "myOwnEngineName"
 *
 * DISCLAIMER:
 * No warranty - use as is or modify but retain the originator's coordinates:
 * CopyRight Andreas Imhof, www.aiedv.ch, ai@aiedv.ch
 */
//
var setTimeout_Task_curfile = new File($.fileName),
setTimeout_Task_curfullname = decodeURI(setTimeout_Task_curfile.name),
                // setTimeout_Taskname must be a UNIQUE name, so we take it from the current running script!! 
                // May be set to any String like
                // setTimeout_Taskname = 'myOwnTask';
setTimeout_Taskname = setTimeout_Task_curfullname.lastIndexOf(".") > 0 ? (setTimeout_Task_curfullname.substr(0,setTimeout_Task_curfullname.lastIndexOf("."))) : setTimeout_Task_curfullname,

setTimeout_Tasks = {},  // all defined tasks prepared to run
/**
 * setTimeout_hasIdleTask
 * Utility function
 * @param {Number} the timeout task id
 * @return {Boolean} true if a given timeout id also has his attached idleTask
 */
setTimeout_hasIdleTask = function(id) {
  var has = false, i;
  for (i = 0; i < app.idleTasks.length; i++) {
    //alert("id: " + id + " tid: " + app.idleTasks[i].label);
    if (app.idleTasks[i].isValid && (app.idleTasks[i].id === id)) {
      has = true;
      break;
    }
  }
  return has;
},
/**
 * setTimeoutList
 * Utility function
 * @return {String} a list of all currently active setTimeout_Tasks
 */
setTimeoutList = function() {
  var list = "", cb,
    k;
  for (k in setTimeout_Tasks) {
    if (list !== "") list += ",";
    cb = setTimeout_Tasks[k]["cb"].toString();
    cb = cb.replace(/\s/g,"");
    list += setTimeout_Tasks[k]["taskid"] + ":" + cb;
  }
  return list;
},
/**
 * idleTasksList
 * Utility function
 * @return {String} a list of all currently active idleTasks
 */
idleTasksList = function() {
  var list = "",
    k;
  for (k = 0; k < app.idleTasks.length; k++) {
    if (list !== "") list += ",";
    list += app.idleTasks[k].id + ":" + setTimeout_hasIdleTask(app.idleTasks[k].id) + ":" + app.idleTasks[k].label;
  }
  return list;
},
/**
 * setTimeoutInit
 * Init/clean the timeout system
 */
setTimeoutInit = function() {
  var it;
  // remove all (erroneous) idleTasks
  //alert("set idleTasks: " + app.idleTasks.length);
  //NA: logmess("setTimeoutInit set idleTasks: " + app.idleTasks.length + "\n");
  for (it = 0; it < app.idleTasks.length; it++) {
    if (app.idleTasks[it].label == setTimeout_Taskname) {
      //alert("removing idleTask id " + app.idleTasks[it].id + " label: " + app.idleTasks[it].label);
      clearTimeout(app.idleTasks[it].id);
    }
  }
  setTimeout_Tasks = {};
},
/**
 * Tasks Handler
 * Check if a task can be called now
 * @param {Number} taskid
 * @return {Boolean} always false
 */
setTimeoutHandler = function(taskid) {
  var now_Ticks = new Date().getTime(),
    cb, cb_retval = undefined;

  try {
    //alert("taskid: " + taskid + "\nnumcalls: " + setTimeout_Tasks[taskid]["numcalls"]);
    // we look for well timed call only!!!  CS6 calls at start AND after the timeout
    if (setTimeout_Tasks[taskid]["end_ticks"] > now_Ticks) {    // we have not reached timeout
      //NA: logmess("setTimeoutHandler id " +  taskid + " too early by ms: " + (setTimeout_Tasks[taskid]["end_ticks"] - now_Ticks) + "\n");
      //alert("setTimeoutHandler id " +  taskid + " too early by ms: " + (setTimeout_Tasks[taskid]["end_ticks"] - now_Ticks));
      setTimeout_Tasks[taskid]["numcalls"] += 1;
      return false; // wait for next call
    }
  }
  catch(ex) {
    alert("Exception (1) in function 'setTimeoutHandler()', taskid " + taskid + ":\n" + ex);
  }

  try {
    cb = setTimeout_Tasks[taskid]["cb"];    // store the callback
    clearTimeout(taskid);   // remove the timeout
  }
  catch(ex) {
    alert("Exception (2) in function 'setTimeoutHandler()', taskid " + taskid + ":\n" + ex);
  }

  try {
    //NA: logmess("setTimeoutHandler call " +  cb + "\n");
    cb_retval = cb();   // call the cb
    //if (cb_retval) alert("cb_retval:\n" + cb_retval);
  } catch(ex) {
    alert("Exception in function '" + cb() + ":\n" + ex);
  }

  return false;
},
/**
 * setTimeout
 * Set a function to called after the given timeout
 * @param {function} callback the function to call
 * @param {Number} timeout in ms
 * @return {Boolean} null on error, otherwise the id (can be used with clearTimeout
 */
setTimeout = function(callback,timeout) {
  try {
    var idle_Task,
      now_Ticks = new Date().getTime();
    idle_Task = app.idleTasks.add({sleep:timeout});
    idle_Task.label = setTimeout_Taskname;
    setTimeout_Tasks[idle_Task.id] = {
      "label": setTimeout_Taskname,
      "start_ticks": now_Ticks,
      "sleep": timeout,
      "end_ticks": now_Ticks + timeout,
      "cb": callback,
      "taskid": idle_Task.id,
      "numcalls": 0
      };
    setTimeout_Tasks[idle_Task.id].handler = function(ev){setTimeoutHandler(setTimeout_Tasks[idle_Task.id]["taskid"]);};
    idle_Task.addEventListener(IdleEvent.ON_IDLE, setTimeout_Tasks[idle_Task.id].handler,false);
    //NA: logmess("setTimeout idle_Task.id: " + idle_Task.id + ", timeout: " + timeout + "\ncallback: " + callback + "\n");
    return idle_Task.id;
  }
  catch(ex) {
    alert("Exception in function 'setTimeout()':\n" + ex);
  }
  return null;
},
/**
 * clearTimeout
 * Clear the timeout given by the setTimeout return value
 * @param {Number} id the timeout id to clear
 */
clearTimeout = function (id){
  var i, task = null;
  for (i = 0; i < app.idleTasks.length; i++) {
    //alert("id: " + id + " tid: " + app.idleTasks[i].label);
    if ((app.idleTasks[i].id == id) && app.idleTasks[i].isValid) {
      task = app.idleTasks[i];
      break;
    }
  }

  if (task !== null) {
    try {
      if (setTimeout_Tasks[id] && setTimeout_Tasks[id].handler) {
        // this kills any!!!    app.idleTasks.itemByID(id).removeEventListener(IdleEvent.ON_IDLE, setTimeout_Tasks[id].handler,false);
        task.removeEventListener(IdleEvent.ON_IDLE, setTimeout_Tasks[id].handler,false);
      }
      // this kills any!!!    app.idleTasks.itemByID(id).remove();
      //task.remove();
      task.sleep = 0;
    }
    catch(ex) {
      alert("Exception in function 'clearTimeout() idleTasks':\n" + ex);
    }
    try {
      delete setTimeout_Tasks[id];
    }
    catch(ex) {
      alert("Exception in function 'clearTimeout() delete setTimeout_Tasks':\n" + ex);
    }
  }
};
/**
 * Init/clean the timeout system
 */
setTimeoutInit();
// alert(setTimeout_Taskname);  // Just to check if the 'setTimeout_Taskname' was set correctly
Håken Lid
  • 22,318
  • 9
  • 52
  • 67
Davide Barranca
  • 339
  • 1
  • 3
  • 14
1

In Adobe After Effects using the $.sleep(time) will cause the script to stall.

Instead use the native app.scheduleTask(stringToExecute, delay, repeat) in ExtendScript:

Example:

var taskId = app.scheduleTask(function () {alert("hello world")}, 1500, false);

To cancel:

app.cancelTask(taskId);
GuZ
  • 21
  • 3
  • how can I avoid `Error: ReferenceError: app is not defined`? – TheKill-996 Feb 18 '21 at 03:03
  • 1
    @TheKill-996 `app` is part of the Extendscript for external .jsx Script files (e.g. not when you use Expressions on Layer Properties). See https://ae-scripting.docsforadobe.dev/general/application/?highlight=scheduletask#app-scheduletask - I think I made a mistake actually, app.setTimeout() is not natively supported, I think one of the standard tutorial panels in After Effects added the routine. – GuZ Feb 19 '21 at 07:32
  • In Photoshop `app.scheduleTask is not a function`. Any idea how to get around this? – Frank Jun 29 '21 at 07:07