简体   繁体   中英

How to wait for a function for a certain delay before executing the next function

Lets say i have two function i need to run

await func1()
await func2()

I want to wait for a maximum of 1000ms for func1() to complete. If it does not complete within the 1000ms then call func2() , don't need to cancel the request for func1()

But if the func1() returns in less than 1000ms, let's say 500ms, then it immediately executes the func2()

I thought of trying some custom sleep function but not sure on how I would exit the sleep before hand.

You can use Promise.race() to clamp the execution time. It accepts multiple promises and returns a single one that will resolve as soon as the first of the promise arguments resolves:

 const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); //mock functions taking time const func1 = () => sleep(100); // 0.100s const func2 = () => sleep(2000); // 2s const func3 = () => sleep(300); // 0.300s //make a function that will clamp the wait time const maxWait = ms => promise => Promise.race([sleep(ms), promise]); async function main() { const noMoreThan1Second = maxWait(1000); console.time("func1"); await noMoreThan1Second(func1()); console.timeEnd("func1"); console.time("func2"); await noMoreThan1Second(func2()); console.timeEnd("func2"); console.time("func3"); await noMoreThan1Second(func3()); console.timeEnd("func3"); } main();

Note that Promise.race() will not cancel the task that the slower promise(s) are linked to. Since promises do not control asynchronous tasks, they are simply a notification mechanism. Therefore, using Promise.race() simply means ignoring the results of the slower promises. The async task they do would still continue in the background and can still succeed or fail. If rejections need to be handled, then nothing changes with Promise.race() :

 const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); async function func() { console.log("func() started"); await sleep(2000); console.log("func() finished"); } async function main() { console.log("calling func()"); await Promise.race([sleep(1000), func()]); console.log("finished waiting for func()"); } main();


As a more advanced usage, default values might be returned if a promise is not resolved in time:

 //allow optional value to be delayed const sleep = (ms, value) => new Promise(resolve => setTimeout(resolve, ms, value)); const maxWait = (ms, value) => promise => Promise.race([sleep(ms, value), promise]); const func1 = () => sleep(100, "one"); const func2 = () => sleep(2000, "two"); async function main() { const noMoreThan1Second = maxWait(1000, "default"); console.log(await noMoreThan1Second(func1())); console.log(await noMoreThan1Second(func2())); } main();

Alternatively, there could be an error if something takes too long:

 //allow optional value to be delayed const sleep = (ms, value) => new Promise(resolve => setTimeout(resolve, ms, value)); const delayedReject = (ms, value) => new Promise((_, reject) => setTimeout(reject, ms, value)); const maxWait = (ms, value) => promise => Promise.race([delayedReject(ms, value), promise]); const func1 = () => sleep(100, "one"); const func2 = () => sleep(2000, "two"); async function main() { const noMoreThan1Second = maxWait(1000, "timeout"); try { console.log(await noMoreThan1Second(func1())); console.log(await noMoreThan1Second(func2())); } catch(e) { console.log("problem encountered:", e) } } main();

As a small note, an alternative implementation of delayedReject() that re-uses sleep() would be:

const delayedReject = (ms, value) =>
    sleep(ms)
        .then(() => Promise.reject(value));

or shorter eta-reduced version:

const delayedReject = (ms, value) =>
    sleep(ms, value)
        .then(Promise.reject);

You can allow a specific time for a promise to complete using the function below. Basically, it polls the promise periodically until it either settles or the timeout expires.

 function wait(promise, maxTime) { let start = Number(new Date) let result = ['', null] promise.then(r => result = ['ok', r]).catch(r => result = ['error', r]) return new Promise((resolve, reject) => { function poll() { if (result[0] === 'ok') return resolve(result[1]) if (result[0] === 'error') return reject(result[1]) let now = Number(new Date) if (now - start > maxTime) return reject('timeout') window.requestAnimationFrame(poll) } poll() }) } // EXAMPLE: wait( fetch('https://jsonplaceholder.typicode.com/posts/1'), 10 ).then(r => console.log('OK', String(r))).catch(r => console.log('ERROR', String(r))) wait( fetch('https://jsonplaceholder.typicode.com/posts/1'), 1000 ).then(r => console.log('OK', String(r))).catch(r => console.log('ERROR', String(r)))

You can use Promise.race function for doing this. Here is an example

 const func1 = new Promise(resolve => { setTimeout(() => { console.log("func1"); resolve("func 1") }, 1200); }) const func2 = () => {console.log("func2")}; const promiseMaximumWait = (promise, time) => { const controlPromise = new Promise((resolve) => { setTimeout(() => resolve("controlPromise"), time) }); return Promise.race([controlPromise, promise]); } promiseMaximumWait(func1, 1000).then((val) => { console.log(val); func2(); })

From MDN Docs

The Promise.race() method returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise.

So we have created a control promise which is a settimeout whose duration can be set by ourself and will act as the maximum time after which we have to move ahead. If the func1 resolves in that time, then we proceed with result from func1 and execute func2 immediately. If the maximum time has elapsed we take the result from controlPromise and execute func2.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM