Help

Re: I need a DELAY method & setTimeout() is undefined (not found Window)

2485 2
cancel
Showing results for 
Search instead for 
Did you mean: 
David_Koontz
6 - Interface Innovator
6 - Interface Innovator

Hey,

I’m a newbie with JavaScript so this might be obvious …

I need a delay(ms) method to slow down the script after 5 API calls (limit 5/min.). Problem is all the JavaScript help on the intertube points to the setTimeout() method of the Window object. But it appears that Window is not instantiated inside the Airtable code block, therefore, no setTimeout() method. What’s a good workaround for this?

What might I be missing - other methods?

Thanks
David

53 Replies 53

Well, I’m not sure if I can split it.
The trigger is when record is updated.
There’s no problem when the record being updated don’t have a dependent record.

When you update the name of the parent, the child get’s triggered as well because it has the parent’s name in a column (the update trigger). I wanted it in a way that the parent script operation gets executed first than the child. That’s why I wanted a delay for the child script.

Can you give more details on what you are trying to accomplish? What do you want your script to do? Are the parent and child records in the same table or different tables?
Do both the parent and child records trigger the same automation?

doesnt work what else can i do?

Brian_Frank
6 - Interface Innovator
6 - Interface Innovator

Hi @kuovonne,

Thanks for clarifying the difference between automation and scripting block. This is a very helpful thread!

I just wanted to respond to a question you asked quite a while ago, as I didn’t see anyone else answering. I have at least one example of a situation in which you might need a delay in an automation…

In my case, I’m fetching from another API that has rate limits. If I exceed those rate limits, I get a status code OK=false, and the automation fails. This is not desirable. I’d like the automation to simply wait for a few seconds and try again so that I can honor the rate limits set by the external API.

You mentioned there may be other ways to achieve this without setTimeout(). Can you advise how you might approach the above scenario?

Thanks for any input!

The most common method of introducing a delay is to change the trigger of the automation to account for the delay.

Another option is to call an api service that is specifically designed to mimic a slow api response. There used to be such a service at http://slowwly.robertomurray.co.uk/, but it is no longer functioning. There are instructions at GitHub - rob-murray/slowwly: A test service to mock a slow api response; responds to get and post me... for how to set up your own. Perhaps you can find another such service.

Another option, is to call the Airtable api itself, awaiting the response. For example, you could create and delete a few records, one at a time. If you have a very large table, you could select the records in the table multiple times. The length of the delay is rather unpredictable, and may not be long enough for your needs. These calls are a complete waste of bandwidth, but without an official method of introducing a delay mid-script, we have to resort to whatever hacks we can.

However, I also suggest that you investigate why you might be exceeding the api’s rate limits. If you have multiple records triggering the same automation in the same narrow window of time, even introducing a delay in the script may not you solve your problem.

I just came up with a way to introduce a delay using date comparisons and a brute-force loop.

function delay(seconds) {
    const startTime = (new Date()).getTime()
    while ((new Date()).getTime() - startTime < seconds * 1000)
        continue
}

Call that with the number of seconds that you want to wait, and it will return when it’s done.

Always learning something new. Creating a new Date instance just to get its time is overkill when the same value is available via Date.now().

function delay(seconds) {
    const startTime = Date.now()
    while (Date.now() - startTime < seconds * 1000)
        continue
}
Alexey_Osipov
4 - Data Explorer
4 - Data Explorer

So just to summarize and fix small errors in @Justin_Barrett answer. The following works for automation scripts perfectly without any error in editor. @Wylan_Osorio @Brian_Frank

/**
* @param {number} seconds
*/
function delay(seconds) {
    const startTime = Date.now()
    while (Date.now() - startTime < seconds * 1000)
        continue
}

delay(10)

Oops! Thanks for catching that typo. I didn’t copy the change from a working script, and just edited it here, and apparently got a little careless in my proofreading. I’ve updated my post to fix the error.

Yeah, Unix time is pretty easy for any engine to grab. I think an even simpler busy loop might be a bit more precise in some edge cases, largely on account of how imprecise the Date API is when it comes to timing milliseconds.

Zealous punctuality probably isn’t a requirement for most Airtable use cases but I’ve been pretty happy with this for the past year:

const timeout = (ms, start = Date.now(), i = 0, x) => {

while (!x) 

i < 1e7 

? i++ 

: x = Date.now() - start > ms

}

Lifted the busy loop idea from StackOverflow and did a bit of testing with more reliable Python libraries last year - this method is usually under 2 ms off, or not at all. setTimeout is way less consistent, supposedly for security reasons. Oh, and while the Typescript linter doesn’t recognize it, setTimeout actually works inside the Scripting block. Obviously, it’s in the prototype chain of the globalConfig object but, also seems to have been inherited by the Scripting app wrapper. Not sure if Airtable failed to fully sanitize it together with the rest of window methods or if this is down to how inconsistent browsers still are when it comes to implementing some aspects of the interpreter.

Perrin_Romney
5 - Automation Enthusiast
5 - Automation Enthusiast

@David_Koontz @Bill_French  Do know if this has been solved yet? With this code:

 

function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
await timeout(1000); // 1 sec pause
}

 

I am throwing this error:

 

ERROR
ReferenceError: setTimeout is not defined
    at <anonymous> on line 16
    at timeout on line 16
    at main on line 19

 

 I'm not a professional code monkey as much as I try to be, so I may just need to load the library or something, not sure.

Sannit_Vartak
5 - Automation Enthusiast
5 - Automation Enthusiast
function delay(ms) {
var limit = new Date();
limit = limit.setMilliseconds(limit.getMilliseconds() + ms);
while ((new Date()) < limit) {
    // do nothing
    ;
}
}
delay(20000); //delay 20 second

 

This will work for Delay just replace value inside Delay()

Dean_Arnold
7 - App Architect
7 - App Architect

Hi All,

Common pain point - Wish they'd just add a delay action, a la Zapier!..

That said, what would be the risk of using a script like this, which seems to run just fine...

function wait(ms) {
return new Promise(resolve => {
const startTime = new Date().getTime();
while (new Date().getTime() < startTime + ms);
resolve();
});
}

async function main() {
console.log('Waiting for 20 seconds...');
await wait(20000);
console.log('Done!');
}

main();
Lu
6 - Interface Innovator
6 - Interface Innovator

A very quick and dirty DIY solution for those who have access to a public web server that runs PHP could be as simple as creating a script with the following:

<?php
sleep(max(0, (int) $_POST['seconds']));

Then do a fetch on it in your automation like so:

await fetch('https://your-site.example/delay.php', {
    method: 'POST',
    body: new URLSearchParams([
        seconds: 55
    ])
});

Seems pretty bizarre that Airtable would rather have users max out their servers' CPUs than provide a sleep function. They could provide a simple network service like slowwly, that's only reachable from automations, to avoid modifications to their JavaScript execution environment.