Asynchronous Scripting in Drafts (async/await, Promises, etc.)

This is an advanced feature, and if you are just writing basic scripts, you probably don’t need to worry about it, but I wanted to draw attention to it’s available for those who are interested.

In release 17, the script action step added a new option to “allow asynchronous execution”.

By default, this option is off for all script steps, in which case they will behave as they always have, and when the end of the script is reached, Drafts will assume it’s operations are completed and it will move on to the next action step - or complete the action if no additional steps are defined.

All Drafts’ own script objects (at least at the moment) are designed to work synchronously and return results, but many JavaScript libraries, or people who are comfortable writing more advanced JavaScript might want to utilize asynchronous operations like Promises, async/await, which may allow execution of the script to continue while completing functions. This option enables those async operations to work in the context of Drafts action steps.

The key difference being, when relying on asynchronous operations in a script, Drafts does not have an implicit way of knowing when a script is complete, so it becomes the responsibility of the script writer to inform Drafts when it’s complete. It does this by calling the script.complete() function somewhere in it’s code.

When a script step with the async option enabled is run, Drafts waits for that script.complete() call to happen before continuing with the action. If you fail to call script.complete(), Drafts might appear to hang and never complete the action. Technically, there is a timeout (currently 60 seconds) where Drafts will assume you forgot to complete the script and it will fail the action, but in practice it is very important you design your script to always result in a script.complete() call.

Excited to see some of the possibilities this enabled in your scripts in the future!

4 Likes

Great feature !

Today, I found a way to integrate it into my actions.

One of them is completing some iOS reminders and adding some new ones depending on what I wrote in a draft.

But this action is long to execute and during the execution, I’m stuck and can not write anything in Drafts. I have to wait for the end of the action until all the reminders are marked as completed and new ones are added.

Thanks to an async function, the action is executed asynchronously and I’m able to continue writing even during its execution.

Thanks for this !

1 Like

Here are the scripts I added into my actions:

// in the first action, I define this
var asyncFunctions = [];

// Then, in any action, I can add into asynFunctions any function to be launched asynchronously
function Test (f) {
  alert(f.text);
}
asyncFunctions.push({name:'Test', f:{text:'Hey great!'}});

// Finally, in the last action (with the option 'allow asynchronous execution), I add this script to execute all asyncFunctions if any
async function asyncExec(funcs) {
  let promise = new Promise((resolve) => {
    for (let i = 0; i < funcs.length; i++) {
      if (funcs[i].name && typeof this[funcs[i].name] === 'function') this[funcs[i].name](funcs[i].f);
    }
    resolve('resolved!');
  });
  let result = await promise;
  script.complete();
}
asyncExec(asyncFunctions);
1 Like