I want to have function which will do some work, if then is some condition true, then resolve promise. If not, wait little bit (lets say one second) and then try it again. Beside that if time limit expired, promise will reject. How can do I that? Look at my code example...If I wrap entire function in return new Promise(), I cant use await inside of it (which I need...I need to sleep some time if condition fail, and also I need wait to my await at end of my function).
Here is my example of code:
async funcWithTimeLimit(numberToDecrement, timeLimit){
let sleep = undefined;
let timeLimitTimeout = setTimeout(() => {
if (sleep)
clearTimeout(sleep);
return Promise.reject("Time limit of " + (timeLimit/1000) +" secs expired"); //reject
}, timeLimit);
while(numberToDecrement > 0){
for(let i = 10;(i > 0 && numberToDecrement > 0); i--){ //Do some work
numberToDecrement--;
}
if(numberToDecrement > 0){
await new Promise(resolve => sleep = setTimeout(resolve, 1000));
}
}
clearTimeout(timeLimitTimeout);
await new Promise((resolve, reject) => sleep = setTimeout(resolve, 500)); // Do something
return ""; //resolve
}
NOTE: My biggest problem is - how can I write this function to be able to catch rejection (at place where I call funcWithTimeLimit()) in case of time limit expiration?
There are two basic approaches. One is to repeatedly check the time and throw an exception once you hit the limit, the other is to race each awaited promise against a timeout. A third would require cooperation by the asynchronous tasks that you start, if they give you a way to cancel them in their interface, to simply offload the task of checking for timeout to them.
In neither of them you can reject the async function by throwing an exception from a setTimeout.
Rather simple:
function doSomething() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 500)); // Do something
}
}
async funcWithTimeLimit(numberToDecrement, timeLimit) {
while (numberToDecrement > 0) {
for (let i = 10; i > 0 && numberToDecrement > 0; i--) {
if (Date.now() > timelimit) throw new TimeoutError("Exceeded limit");
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
numberToDecrement--; // Do some work
}
if (numberToDecrement > 0) {
if (Date.now() + 1000 > timelimit) throw new TimeoutError("Exceeded limit");
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
// but ineffective here:
await doSomething();
return "";
}
Racing:
async funcWithTimeLimit(numberToDecrement, timeLimit) {
const timeout = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new TimeoutError("Exceeded limit"));
}, timeLimit - Date.now());
});
timeout.catch(e => void e); // avoid unhandled promise rejection if not needed
while (numberToDecrement > 0) {
for (let i = 10; i > 0 && numberToDecrement > 0; i--) {
// no guarding of synchronous loops
numberToDecrement--; // Do some work
}
if (numberToDecrement > 0) {
await Promise.race([ timeout,
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
new Promise(resolve => setTimeout(resolve, 1000)),
]);
}
}
await Promise.race([ timeout,
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
doSomething(),
]);
return "";
}
Notice that the setTimeout keeps the event loop open if when the function already completed, we just ignore the rejected promise. Better, but more laborious, is to cancel the timeout when we don't need it any longer:
async funcWithTimeLimit(numberToDecrement, timeLimit) {
let timer;
const timeout = new Promise((resolve, reject) => {
timer = setTimeout(() => {
reject(new TimeoutError("Exceeded limit"));
}, timeLimit - Date.now());
});
try {
// as before:
while (numberToDecrement > 0) {
for (let i = 10; i > 0 && numberToDecrement > 0; i--) {
numberToDecrement--; // Do some work
}
if (numberToDecrement > 0) {
await Promise.race([ timeout, new Promise(resolve => setTimeout(resolve, 1000)) ]);
}
}
await Promise.race([ timeout, doSomething() ]);
return "";
} finally {
clearTimeout(timer);
// ^^^^^^^^^^^^^^^^^^^^
}
}
With cooperation from the called function it's better of course:
function delay(t, limit) {
if (Date.now() + t > timelimit) throw new TimeoutError("Exceeded limit");
return new Promise(resolve => setTimeout(resolve, t));
}
function doSomething(limit) {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
xhr.cancel(); // custom API-dependent cancellation
reject(new TimeoutError("Exceeded limit"));
}, limit - Date.now());
const xhr = someApiCall(); // Do something
xhr.onerror = err => { clearTimeout(timeout); reject(err); };
xhr.onload = res => { clearTimeout(timeout); resolve(res); };
}
}
async funcWithTimeLimit(numberToDecrement, timeLimit) {
while (numberToDecrement > 0) {
for (let i = 10; i > 0 && numberToDecrement > 0; i--) {
numberToDecrement--; // Do some work
}
if (numberToDecrement > 0) {
await delay(1000, timeLimit);
// ^^^^^^^^^
}
}
await doSomething(timeLimit);
// ^^^^^^^^^
return "";
}
You can (where applicable) and should (where sensible) combine these approaches of course.
Related
my function greetings works but when i try to return a promise to execute things once its done im getting nothing, what am i missing?
I have tried putting return resolve() as to make sure the function ends but still nothing, I can´t get the .then() to execute.
const greetingDivs = [firstDiv, secondDiv, thirdDiv, fourthDiv, fifthDiv]
let y = 0
function greetings() {
return new Promise((resolve, reject) => {
if (y == greetingDivs.length) {
// console.log('resolved')
resolve('done')
}
if (y != greetingDivs.length) {
setTimeout(() => {
let lastDiv = consoleOutput.appendChild(greetingDivs[y])
.scrollIntoView(false, { behavior: 'smooth' })
y++
greetings()
}, 300)
}
})
}
greetings().then(() => {
console.log('hello')
})
Your code only resolves one promise, while it creates 5. Notably, the first one, the one that greetings() returns, never resolves.
I would suggest promisifying setTimeout, so you have that logic once and for all. Then the looping can be done in an async function with a for loop and await:
const consoleOutput = document.body;
const [firstDiv, secondDiv, thirdDiv, fourthDiv, fifthDiv] = [
"Hello", "Welcome to this demo", "Hope it suits your case", "Test it", "and enjoy"].map(s => {
const div = document.createElement("div");
div.textContent = s;
return div;
});
const greetingDivs = [firstDiv, secondDiv, thirdDiv, fourthDiv, fifthDiv];
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
async function greetings() {
for (let div of greetingDivs) {
consoleOutput.appendChild(div)
.scrollIntoView(false, { behavior: 'smooth' });
await delay(300);
}
}
greetings().then(() => {
console.log('hello');
});
See ES6 promise for loop for more ideas on how you can create such loops.
I have to keep making calls to SERVICE_PATH until the report in the response ready.
Apparently, below code is blocking due to the usage of "do/while".
So, the question is how can I restructure the code to make it non-blocking?
let scoring_report;
let timeout_count = 1;
let getReport;
do {
await new Promise((resolve) => setTimeout(resolve, timeout_count * 1000));
getReport = await axios.post(`${process.env.SERVICE_PATH}:8001`, {body});
scoring_report = getReport.data.data.reportBase64;
timeout_count += 2;
} while (!scoring_report);
Your code is blocking not due to do... while... Its due to the async await.
Sample blocking code.
async function resolveAfterSomeTime() {
let counter = 0;
do {
console.log("starting promise")
const x = await new Promise(resolve => {
setTimeout(function () {
resolve("done")
console.log("promise is done")
}, 800)
});
counter++;
console.log(x);
} while (counter < 5);
}
resolveAfterSomeTime();
As you can see the above code is blocking for 800 milli second for each execution due to async await.
You can make this non blocking by simply removing async await. If you are not interested in the result, you can simply call the function and go for the next iterearion. Here in the below code, the value for x will be a promise, you can check the resolved status of the promise to handle your further logic using x.then((resp) => console.log(resp))
Sample code.
function resolveAfterSomeTime() {
let counter = 0;
do {
console.log("starting promise")
const x = new Promise(resolve => {
setTimeout(function () {
console.log("promise is done");
resolve("done");
}, 800)
});
counter++;
x.then((resp) => console.log(resp))
} while (counter < 5);
}
resolveAfterSomeTime();
Your sample non blocking code
let scoring_report;
let timeout_count = 1;
let getReport;
do {
new Promise((resolve) => setTimeout(resolve, timeout_count * 1000));
// here getReport will be a promise
getReport = axios.post(`${process.env.SERVICE_PATH}:8001`, { body });
// Use getReport.then((data) => {}) for further logic
} while (!scoring_report);
Implementation using setInterval
If you just want to execute the function on the specified interval, you can make use of setInterval which will not block your main thread. Donot forget to clearInterval once the required result is available.
Sample Implementation
let scoring_report;
let timeout_count = 1;
let getReport;
const myInterval = setInterval(() => {
getReport = await axios.post(`${process.env.SERVICE_PATH}:8001`, { body });
scoring_report = getReport.data.data.reportBase64;
timeout_count += 2;
if(scoring_report) {
clearInterval(myInterval)
}
}, timeout_count * 1000);
function page() {
return new Promise((resolve, reject) => {
setTimeout(function() {
fo = $("#root>div>div>main>div>div:nth-child(3)>div>div:nth-child(2)>div>div>div:nth-child(2)>div:nth-child(2)>div>div:nth-child(2)>div>div:nth-child(2)>div>ul>li:nth-last-child(1)")
fo.click()
console.log('翻页')
resolve();
}, 200)
})
}
function l() {
for (let i = 0, p = Promise.resolve(); i < 10; i++) {
p = p.then(() => {
return new Promise(resolve =>
setTimeout(function() {
console.log('wo')
$('#root>div>div>main>div>div:nth-child(3)>div>div:nth-child(2)>div>div>div:nth-child(2)>div:nth-child(2)>div>div:nth-child(2)>div>div:nth-child(1)>div:nth-child(1)>table>tbody>tr>td:nth-child(7)>div>div:nth-child(2)>a>span').eq(i).click()
resolve();
}, 200)
)
})
.then(() => {
return new Promise(resolve =>
setTimeout(function() {
console.log('wocao')
$('body>div:nth-last-child(1)>div>div>div:nth-child(3)>div:nth-child(2)>button>span').click()
resolve();
}, 200)
)
}
)
}
}
Promise.resolve().then(l).then(page)
Why is the program working not in order of the promises? How can I solve this problem? I've tried many times but they still execute together but not in order. Could somebody teach me? Very thanks.
Your function l is a unit function (i.e. returns nothing) and is therefore not awaitable as it does not return a Promise (either explicitly, or via being marked as async).
You should:
function l() {
let p = Promise.resolve() // this needs to exist out of the loop scope
for (let i = 0; i < 10; i++) {
p = p.then( /* ... */ )
}
return p; // <-- so it can be returned here
}
You need to resolve inside the timeout
new Promise(function(resolve, reject) {
// the function is executed automatically when the promise is constructed
// after 1 second signal that the job is done with the result "done"
setTimeout(() => {console.log("done");resolve()}, 1000);
}).then(function (res2, rej2) {
setTimeout(() => {console.log("done2")}, 1000);
})
but in your case you do not define parameters to the promise, hence they are default.
I have a Subject, which used to be fire async chain.
Its sometimes looks like working correctly, sometimes looks like the subscribe doesn't wait for the resolve.
behavior.subscribe(async (result)=> {
await fnc1(result);
});
fnc1(in) {
return new Promise(async (resolve,reject) => {
for(let w of in.split(',')) {
await fnc2(w);
}
//if for finished, go to next subscribed value
resolve();
});
}
fnc2(w) {
return new Promise((resolve,reject) => {
let i = 0;
setInterval( () => {
i = i + 1;
if(i == 10) {
//go next value in for loop
resolve();
}
},100);
});
}
Not an entirely serious question, more of a shower thought: JavaScript's await keyword should allow for something that feels an awful lot like a mutex in your average "concurrent language".
function Mutex() {
var self = this; // still unsure about how "this" is captured
var mtx = new Promise(t => t()); // fulfilled promise ≡ unlocked mutex
this.lock = async function() {
await mtx;
mtx = new Promise(t => {
self.unlock = () => t();
});
}
}
// Lock
await mutex.lock();
// Unlock
mutex.unlock();
Is this a correct implementation (apart from proper error handling)? And… can I have C++-RAII-style lock guards?
Your implementation allows as many consumers obtain the lock as ask for it; each call to lock waits on a single promise:
function Mutex() {
var self = this; // still unsure about how "this" is captured
var mtx = new Promise(t => t()); // fulfilled promise ≡ unlocked mutex
this.lock = async function() {
await mtx;
mtx = new Promise(t => {
self.unlock = () => t();
});
}
}
const mutex = new Mutex();
(async () => {
await Promise.resolve();
await mutex.lock();
console.log("A got the lock");
})();
(async () => {
await Promise.resolve();
await mutex.lock();
console.log("B got the lock");
})();
You'd need to implement a queue of promises, creating a new one for each lock request.
Side notes:
new Promise(t => t()) can be more simply and idiomatically written Promise.resolve() :-)
No need for self if you're using arrow functions like that; arrow functions close over the this where they're created (exactly like closing over a variable)
Probably would make sense for unlock to be a resolution value of the lock promise, so only the code that obtained the lock can release it
Something like this:
function Mutex() {
let current = Promise.resolve();
this.lock = () => {
let _resolve;
const p = new Promise(resolve => {
_resolve = () => resolve();
});
// Caller gets a promise that resolves when the current outstanding
// lock resolves
const rv = current.then(() => _resolve);
// Don't allow the next request until the new promise is done
current = p;
// Return the new promise
return rv;
};
}
Live Example:
"use strict";
function Mutex() {
let current = Promise.resolve();
this.lock = () => {
let _resolve;
const p = new Promise(resolve => {
_resolve = () => resolve();
});
// Caller gets a promise that resolves when the current outstanding
// lock resolves
const rv = current.then(() => _resolve);
// Don't allow the next request until the new promise is done
current = p;
// Return the new promise
return rv;
};
}
const rand = max => Math.floor(Math.random() * max);
const delay = (ms, value) => new Promise(resolve => setTimeout(resolve, ms, value));
const mutex = new Mutex();
function go(name) {
(async () => {
console.log(name + " random initial delay");
await delay(rand(50));
console.log(name + " requesting lock");
const unlock = await mutex.lock();
console.log(name + " got lock");
await delay(rand(1000));
console.log(name + " releasing lock");
unlock();
})();
}
go("A");
go("B");
go("C");
go("D");
.as-console-wrapper {
max-height: 100% !important;
}
Is this a correct implementation?
No. If two tasks (I can't say "threads") try to do mutex.lock() while it is currently locked, they will both get the lock at the same time. I doubt that's what you want.
A mutex in JS is really just a boolean flag - you check it, you set it when you acquire a lock, you clear it when you release the lock. There's no special handling of race conditions between checking and acquiring as you can do it synchronously in single-threaded JS, without any other threads interfering.
What you seem to be looking for however is a queue, i.e. something where you can schedule yourself to get the lock and will be notified (through a promise) when the previous lock is released.
I'd do that with
class Mutex {
constructor() {
this._lock = null;
}
isLocked() {
return this._lock != null;
}
_acquire() {
var release;
const lock = this._lock = new Promise(resolve => {
release = resolve;
});
return () => {
if (this._lock == lock) this._lock = null;
release();
};
}
acquireSync() {
if (this.isLocked()) throw new Error("still locked!");
return this._acquire();
}
acquireQueued() {
const q = Promise.resolve(this._lock).then(() => release);
const release = this._acquire(); // reserves the lock already, but it doesn't count
return q; // as acquired until the caller gets access to `release` through `q`
}
}
Demo:
class Mutex {
constructor() {
this._lock = Promise.resolve();
}
_acquire() {
var release;
const lock = this._lock = new Promise(resolve => {
release = resolve;
});
return release;
}
acquireQueued() {
const q = this._lock.then(() => release);
const release = this._acquire();
return q;
}
}
const delay = t => new Promise(resolve => setTimeout(resolve, t));
const mutex = new Mutex();
async function go(name) {
await delay(Math.random() * 500);
console.log(name + " requests lock");
const release = await mutex.acquireQueued();
console.log(name + " acquires lock");
await delay(Math.random() * 1000);
release()
console.log(name + " releases lock");
}
go("A");
go("B");
go("C");
go("D");
I recommend using a library like async-mutex:
const mutex = new Mutex();
// ...
const release = await mutex.acquire();
try {
// ...
} finally {
release();
}