Loop on a promise indefinitely until rejection - javascript

I have a function that does some async work and returns a Promise, and I want to execute this function indefinitely until the promise is rejected.
Something like the following :
doSomethingAsync().
.then(doSomethingAsync)
.then(doSomethingAsync)
.then(doSomethingAsync)
// ... until rejection
I made a little CodePen so I can test potential solutions : http://codepen.io/JesmoDrazik/pen/pbAovZ?editors=0011
I found several potential answers but nothing seems to work for my case.
If anyone has a solution, I'd be glad, because I just can't find anything !
Thanks.

You can do
(function loop(){
doSomethingAsync().then(loop);
})();
But looking at your pen it's not clear where the rejection should come from. If you want to stop repeating an operation when the user clicks a button, you can change a state in the button handling and then do
(function loop(){
doSomethingAsync().then(function(){
if (!stopped) loop();
});
})();

Made a modification to your codepen
var FAKE_COUNT = 0;
function doSomething() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('doing something async: ' + FAKE_COUNT)
if (FAKE_COUNT < 10) {
resolve();
} else {
reject();
}
FAKE_COUNT ++;
}, 1000);
});
}
const button = document.querySelector('.js-button');
button.addEventListener('click', () => {
// maybe we can do something here ?
})
function test() {
doSomething().then(test).catch(() => {
console.log("Rejected")
});
}
test();
FAKE_COUNT just becomes the flag, so if you want to stop the promise with a click instead of a count you could just make it a bool and check that when executing the async task

Depending on where you want the logic and rejection to take place, but assuming you want the promise itself to be self executing (otherwise the asynchronous execution itself could loop) it could return itself as a new promise:
function doSomething() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('doing something async');
resolve();
}, 1000);
}).then(()=>doSomething());
}
For the rejection part, the easiest way would be to introduce a flag, (although that would make the promise a little less self containing):
var stop = false;
function doSomething() {
return new Promise((resolve, reject) => {
if(stop)
reject();
else{
setTimeout(() => {
console.log('doing something async');
resolve(); //(check for stop could be done here as well)
}, 1000);
}
}).then(()=>doSomething());
}
const button = document.querySelector('.js-button');
button.addEventListener('click', () => {
stop = true;
});
doSomething();

Related

Trying to use `debounce` to throttle API requests, but getting "js Uncaught (in promise) TypeError: [function] is undefined" [duplicate]

I'm trying to implement a debounce function that works with a promise in javascript. That way, each caller can consume the result of the "debounced" function using a Promise. Here is the best I have been able to come up with so far:
function debounce(inner, ms = 0) {
let timer = null;
let promise = null;
const events = new EventEmitter(); // do I really need this?
return function (...args) {
if (timer == null) {
promise = new Promise(resolve => {
events.once('done', resolve);
});
} else {
clearTimeout(timer);
}
timer = setTimeout(() => {
events.emit('done', inner(...args));
timer = null;
}, ms);
return promise;
};
}
Ideally, I would like to implement this utility function without introducing a dependency on EventEmitter (or implementing my own basic version of EventEmitter), but I can't think of a way to do it. Any thoughts?
I found a better way to implement this with promises:
function debounce(inner, ms = 0) {
let timer = null;
let resolves = [];
return function (...args) {
// Run the function after a certain amount of time
clearTimeout(timer);
timer = setTimeout(() => {
// Get the result of the inner function, then apply it to the resolve function of
// each promise that has been created since the last time the inner function was run
let result = inner(...args);
resolves.forEach(r => r(result));
resolves = [];
}, ms);
return new Promise(r => resolves.push(r));
};
}
I still welcome suggestions, but the new implementation answers my original question about how to implement this function without a dependency on EventEmitter (or something like it).
In Chris's solution all calls will be resolved with delay between them, which is good, but sometimes we need resolve only last call.
In my implementation, only last call in interval will be resolved.
function debounce(f, interval) {
let timer = null;
return (...args) => {
clearTimeout(timer);
return new Promise((resolve) => {
timer = setTimeout(
() => resolve(f(...args)),
interval,
);
});
};
}
And the following typescript(>=4.5) implementation supports aborted features:
Support aborting promise via reject(). If we don't abort it, it cannot execute finally function.
Support custom reject abortValue.
If we catch error, we may need to determine if the error type is Aborted
/**
*
* #param f callback
* #param wait milliseconds
* #param abortValue if has abortValue, promise will reject it if
* #returns Promise
*/
export function debouncePromise<T extends (...args: any[]) => any>(
fn: T,
wait: number,
abortValue: any = undefined,
) {
let cancel = () => { };
// type Awaited<T> = T extends PromiseLike<infer U> ? U : T
type ReturnT = Awaited<ReturnType<T>>;
const wrapFunc = (...args: Parameters<T>): Promise<ReturnT> => {
cancel();
return new Promise((resolve, reject) => {
const timer = setTimeout(() => resolve(fn(...args)), wait);
cancel = () => {
clearTimeout(timer);
if (abortValue!==undefined) {
reject(abortValue);
}
};
});
};
return wrapFunc;
}
/**
// deno run src/utils/perf.ts
function add(a: number) {
return Promise.resolve(a + 1);
}
const wrapFn= debouncePromise(add, 500, 'Aborted');
wrapFn(2).then(console.log).catch(console.log).finally(()=>console.log('final-clean')); // Aborted + final-clean
wrapFn(3).then(console.log).catch(console.log).finally(()=>console.log('final-clean')); // 4 + final_clean
Note:
I had done some memory benchmarks, huge number of pending promises won't cause memory leak. It seems that V8 engine GC will clean unused promises.
I landed here because I wanted to get the return value of the promise, but debounce in underscore.js was returning undefined instead. I ended up using lodash version with leading=true. It works for my case because I don't care if the execution is leading or trailing.
https://lodash.com/docs/4.17.4#debounce
_.debounce(somethingThatReturnsAPromise, 300, {
leading: true,
trailing: false
})
resolve one promise, cancel the others
Many implementations I've seen over-complicate the problem or have other hygiene issues. In this post we will write our own debounce. This implementation will -
have at most one promise pending at any given time (per debounced task)
stop memory leaks by properly cancelling pending promises
resolve only the latest promise
demonstrate proper behaviour with live code demos
We write debounce with its two parameters, the task to debounce, and the amount of milliseconds to delay, ms. We introduce a single local binding for its local state, t -
function debounce (task, ms) {
let t = { promise: null, cancel: _ => void 0 }
return async (...args) => {
try {
t.cancel()
t = deferred()
await t.promise
await task(...args)
}
catch (_) { /* prevent memory leak */ }
}
}
We depend on a reusable deferred function, which creates a new promise that resolves in ms milliseconds. It introduces two local bindings, the promise itself, an the ability to cancel it -
function deferred (ms) {
let cancel, promise = new Promise((resolve, reject) => {
cancel = reject
setTimeout(resolve, ms)
})
return { promise, cancel }
}
click counter example
In this first example, we have a button that counts the user's clicks. The event listener is attached using debounce, so the counter is only incremented after a specified duration -
// debounce, deferred
function debounce (task, ms) { let t = { promise: null, cancel: _ => void 0 }; return async (...args) => { try { t.cancel(); t = deferred(ms); await t.promise; await task(...args); } catch (_) { console.log("cleaning up cancelled promise") } } }
function deferred (ms) { let cancel, promise = new Promise((resolve, reject) => { cancel = reject; setTimeout(resolve, ms) }); return { promise, cancel } }
// dom references
const myform = document.forms.myform
const mycounter = myform.mycounter
// event handler
function clickCounter (event) {
mycounter.value = Number(mycounter.value) + 1
}
// debounced listener
myform.myclicker.addEventListener("click", debounce(clickCounter, 1000))
<form id="myform">
<input name="myclicker" type="button" value="click" />
<output name="mycounter">0</output>
</form>
live query example, "autocomplete"
In this second example, we have a form with a text input. Our search query is attached using debounce -
// debounce, deferred
function debounce (task, ms) { let t = { promise: null, cancel: _ => void 0 }; return async (...args) => { try { t.cancel(); t = deferred(ms); await t.promise; await task(...args); } catch (_) { console.log("cleaning up cancelled promise") } } }
function deferred (ms) { let cancel, promise = new Promise((resolve, reject) => { cancel = reject; setTimeout(resolve, ms) }); return { promise, cancel } }
// dom references
const myform = document.forms.myform
const myresult = myform.myresult
// event handler
function search (event) {
myresult.value = `Searching for: ${event.target.value}`
}
// debounced listener
myform.myquery.addEventListener("keypress", debounce(search, 1000))
<form id="myform">
<input name="myquery" placeholder="Enter a query..." />
<output name="myresult"></output>
</form>
Here's my version in typescript (mostly based on Chris one), if someone need it 😉
function promiseDebounce (exec: (...args: any[]) => Promise<any>, interval: number): () => ReturnType<typeof exec> {
let handle: number | undefined;
let resolves: Array<(value?: unknown) => void> = [];
return async (...args: unknown[]) => {
clearTimeout(handle);
handle = setTimeout(
() => {
const result = exec(...args);
resolves.forEach(resolve => resolve(result));
resolves = [];
},
interval
);
return new Promise(resolve => resolves.push(resolve));
};
}
No clue what you are trying to accomplish as it vastly depends on what your needs are. Below is something somewhat generic though. Without a solid grasp of what is going on in the code below, you really might not want to use it though.
// Debounce state constructor
function debounce(f) {
this._f = f;
return this.run.bind(this)
}
// Debounce execution function
debounce.prototype.run = function() {
console.log('before check');
if (this._promise)
return this._promise;
console.log('after check');
return this._promise = this._f(arguments).then(function(r) {
console.log('clearing');
delete this._promise; // remove deletion to prevent new execution (or remove after timeout?)
return r;
}.bind(this)).catch(function(r) {
console.log('clearing after rejection');
delete this._promise; // Remove deletion here for as needed as noted above
return Promise.reject(r); // rethrow rejection
})
}
// Some function which returns a promise needing debouncing
function test(str) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('test' + str);
resolve();
}, 1000);
});
}
a = new debounce(test); // Create debounced version of function
console.log("p1: ", p1 = a(1));
console.log("p2: ", p2 = a(2));
console.log("p1 = p2", p1 === p2);
setTimeout(function() {
console.log("p3: ", p3 = a(3));
console.log("p1 = p3 ", p1 === p3, " - p2 = p3 ", p2 === p3);
}, 2100)
View the console when running the code above. I put a few messages to show a bit about what is going on. First some function which returns a promise is passed as an argument to new debounce(). This creates a debounced version of the function.
When you run the debounced function as the code above does (a(1), a(2), and a(3)) you will notice during processing it returns the same promise instead of starting a new one. Once the promise is complete it removes the old promise. In code above I wait for the timeout manually with setTimeout before running a(3).
You can clear the promise in other ways as well, like adding a reset or clear function on debounce.prototype to clear the promise at a different time. You could also set it to timeout. The tests in the console log should show p1 and p2 get the same promise (reference comparison "===" is true) and that p3 is different.
Here is what I came up with to solve this issue. All calls to the debounced function batched to the same invocation all return the same Promise that resolves to the result of the future invocation.
function makeFuture() {
let resolve;
let reject;
let promise = new Promise((d, e) => {
resolve = d;
reject = e;
});
return [promise, resolve, reject];
}
function debounceAsync(asyncFunction, delayMs) {
let timeout;
let [promise, resolve, reject] = makeFuture();
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(async () => {
const [prevResolve, prevReject] = [resolve, reject];
[promise, resolve, reject] = makeFuture();
try {
prevResolve(await asyncFunction.apply(this, args));
} catch (error) {
prevReject(error);
}
}, delayMs);
return promise;
}
}
const start = Date.now();
const dog = {
sound: 'woof',
bark() {
const delay = Date.now() - start;
console.log(`dog says ${this.sound} after ${delay} ms`);
return delay;
},
};
dog.bark = debounceAsync(dog.bark, 50);
Promise.all([dog.bark(), dog.bark()]).then(([delay1, delay2]) => {
console.log(`Delay1: ${delay1}, Delay2: ${delay2}`);
});
Both Chris and Николай Гордеев have good solutions. The first will resolve all of them. The problem is that they all be resolved, but usually you wouldn't want all of them to run.
The second solution solved that but created a new problem - now you will have multiple awaits. If it's a function that is called a lot (like search typing) you might have a memory issue. I fixed it by creating the following asyncDebounce that will resolve the last one and reject (and the awaiting call will get an exception that they can just catch).
const debounceWithRejection = (
inner,
ms = 0,
reject = false,
rejectionBuilder
) => {
let timer = null;
let resolves = [];
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
const resolvesLocal = resolves;
resolves = [];
if (reject) {
const resolve = resolvesLocal.pop();
resolve.res(inner(...args));
resolvesLocal.forEach((r, i) => {
!!rejectionBuilder ? r.rej(rejectionBuilder(r.args)) : r.rej(r.args);
});
} else {
resolvesLocal.forEach((r) => r.res(inner(...args)));
}
resolves = [];
}, ms);
return new Promise((res, rej) =>
resolves.push({ res, rej, args: [...args] })
);
};
};
The rejection logic is optional, and so is the rejectionBuilder. It's an option to reject with specific builder so you will know to catch it.
You can see runing example.
This may not what you want, but can provide you some clue:
/**
* Call a function asynchronously, as soon as possible. Makes
* use of HTML Promise to schedule the callback if available,
* otherwise falling back to `setTimeout` (mainly for IE<11).
* #type {(callback: function) => void}
*/
export const defer = typeof Promise=='function' ?
Promise.resolve().then.bind(Promise.resolve()) : setTimeout;

can you resolve a promise in an if statement?

I'm quite new to using promises in JS, and im trying to have a function execute before executing some more code in another function. Only issue is that the promised function uses an if statement to loop a setTimeout command. I added an if statement to make sure the function is done looping before i resolve the promise but the resolve just isn't doing anything. I used console.log to make sure that the if statement is executing and it has no problems printing to the console on either side of the resolve. Any help would be greatly appreciated.
Code:
async makeToast(loader, toaster){
toaster.texture = loader.resources['toaster_down'].texture;
this.interactive = false;
this.x = toaster.x;
this.y = toaster.y - 100;
let transform = {y: this.y};
let popDown = new TWEEN.Tween(transform)
.to({y: toaster.y - 50}, 200)
.onUpdate(() => this.y = transform.y);
popDown.start();
await this.changeTexture(loader, toaster.setting)
console.log('toasting done');
this.interactive = true;
}
changeTexture(loader, setting){
return new Promise((resolve, reject) => {
setTimeout(() => {
this.state++;
this.texture = loader.resources[`bread${this.state}`].texture;
if(this.state < setting) this.changeTexture(loader, setting);
else if(this.state == setting) resolve();
}, 1000);
});
}
After the first setTimeout callback executes, you will never resolve the outermost call's returned promise. You will resolve the innermost call's returned promise eventually, but that does nothing since the promise returned from there is never used.
You could write if (this.state < setting) resolve(this.changeTexture(loader, setting)) but I'd recommend a different, far less confusing (and non-recursive) way instead:
// This could be defined globally, can be useful elsewhere too
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
// This is in your object
async changeTexture (loader, setting) {
while (this.state < setting) {
await delay(1000)
this.state++
this.texture = loader.resources[`bread${this.state}`].texture
}
}
Here I've made the changeTexture function async as well, so we can use await inside and therefore implement the delay in a more straight-forward manner and can build a regular while loop around the whole thing.
(Note: Technically your existing code does the first iteration unconditionally, so a do ... while would be more accurate, but I'm assuming that is just a result of the way you tried building it with setTimeout and not really what you need.)
You can as long as there is a closure linking the resolve variable in the Promise constructor with the resolve() you call in your if statement. However in your code you don't have this:
class SomeClass {
// ...
changeTexture(loader, setting){
return new Promise((resolve, reject) => {
setTimeout(() => {
this.state++;
this.texture = loader.resources[`bread${this.state}`].texture;
if(this.state < setting)
this.changeTexture(loader, setting); <----------.
else if(this.state == setting) |
resolve(); <-- There is a closure to this /
}, 1000); /
}); .---------------------------'
} |
} However this function call will have it's own
"resolve" variable that is no longer captured
by this closure.
This means that when the if/else finally calls resolve() that resolve has nothing to do with the Promise you returned when you call changeTexture().
The way to do what you want is to not call changeTexture recursively so that you maintain a closure between the Promise's resolve variable and the resolve you finally call. To do this simply separate your setTimeout callback from the main changeTexture function:
class SomeClass {
// ...
changeTexture(loader, setting){
return new Promise((resolve, reject) => {
let loop = () => { // use arrow function to capture "this"
this.state++;
this.texture = loader.resources[`bread${this.state}`].texture;
if(this.state < setting) setTimeout(loop, 1000);
else if(this.state == setting) resolve();
}
loop();
});
}
}
Alternatively for minimal change in code you can get your code working by changing only one line:
class SomeClass {
// ...
changeTexture(loader, setting){
return new Promise((resolve, reject) => {
setTimeout(() => {
this.state++;
this.texture = loader.resources[`bread${this.state}`].texture;
if(this.state < setting) setTimeout(arguments.callee(),1000); // <----THIS
else if(this.state == setting) resolve();
}, 1000);
});
}
}
The arguments.callee variable points to the () => {... function you pass to setTimeout. However, arguments.callee is deprecated and is disabled in strict mode so use the loop function above if possible.
You can call resolve and reject from anywhere in your code you wish. But you must call exactly one of them exactly once from within your Promise.
Your sample code doesn't do that when your if-condition is false, so you need to fix that.

Calling a function as many times as possible in a given time interval

I am trying to call the function test() as many times as possible in a given time interval.
Here the function should be running for 15 seconds.
function test(): void; // Only type def
function run() {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 15000); // 15 seconds
while (true) {
test();
}
});
}
run()
.then(() => {
console.log('Ended');
});
However, the function doesn't stop running, and the Ended console.log does not appear. (Promise not resolved obviously). Is there a way to achieve this in Javascript ?
I was wondering, I could probably use console timers and put the condition in the while statement ? (But is that the best way ?)
The reason why your function does not stop executing is because resolving a promise does not stop script executing. What you want is to store a flag somewhere in your run() method, so that you can flip the flag once the promise is intended to be resolved.
See proof-of-concept below: I've shortened the period to 1.5s and added a dummy test() method just for illustration purpose:
let i = 0;
function test() {
console.log(`test: ${i++}`);
}
function run() {
return new Promise(resolve => {
let shouldInvoke = true;
setTimeout(() => {
shouldInvoke = false;
resolve();
}, 1500); // 15 seconds
const timer = setInterval(() => {
if (shouldInvoke)
test();
else
window.clearInterval(timer);
}, 0);
});
}
run()
.then(() => {
console.log('Ended');
});

Test that a function awaits something before doing anything else

Suppose I have a function that executes an asynchronous action (doStuffAsync()) and then intends to do some other stuff (doOtherStuff()).
doStuffAsync() returns a Promise.
Also assume everything is mockable.
How do I test that my function awaits doStuffAsync() before trying to doOtherStuff()?
I thought of mocking doStuffAsync() using resolve => setTimeout(resolve(), timeout), but timeout-based testing looks very fragile.
You need a flag accessible by both doStuffAsync and doOtherStuff.
In doStuffAsync() write in that flag
In doOtherStuff() read from that flag and determine if it was written
Something like:
var isAsyncRunning = false;
function doStuffAsync(){
isAsyncRunning = true;
new Promise(function(resolve, reject) {
setTimeout(()=>{
isAsyncRunning = false;
resolve(); //irrelevant in this exercise
}, 1000);
});
}
doStuffAsync();
function doOtherStuff(){
if(isAsyncRunning){
console.log("Async is running.");
} else {
console.log("Async is no longer running.");
};
}
doOtherStuff();
setTimeout(() => {
//calling doOtherStuff 2 seconds later..
doOtherStuff();
}, 2000);
I managed to complete it with a less ugly solution than setTimeout – setImmediate.
function testedFunction() {
await MyModule.doStuffAsync();
MyModule.doOtherStuff();
}
it('awaits the asynchronous stuff before doing anything else', () => {
// Mock doStuffAsync() so that the promise is resolved at the end
// of the event loop – which means, after the test.
// -
const doStuffAsyncMock = jest.fn();
const delayedPromise = new Promise<void>(resolve => setImmediate(resolve()));
doStuffAsyncMock.mockImplementation(() => delayedPromise);
const doOtherStuffMock = jest.fn();
MyModule.doStuffAsync = doStuffAsyncMock;
MyModule.doOtherStuffMock = doOtherStuffMock;
testedFunction();
expect(doOtherStuffMock).toHaveBeenCalledTimes(0);
}
setImmediate will put off the resolution of your promise to the end of the event loop, which is after your test completes.
So, your assert that doOtherStuff() was not invoked:
Will pass if there is an await inside the testedFunction
Will fail if there isn't.

ES6 promises with timeout interval

I'm trying to convert some of my code to promises, but I can't figure out how to chain a new promise inside a promise.
My promise function should check the content of an array every second or so, and if there is any item inside it should resolve. Otherwise it should wait 1s and check again and so on.
function get(){
return new Promise((resolve) => {
if(c.length > 0){
resolve(c.shift());
}else{
setTimeout(get.bind(this), 1000);
}
});
}
let c = [];
setTimeout(function(){
c.push('test');
}, 2000);
This is how I expect my get() promise function to work, it should print "test" after 2 or 3 seconds max:
get().then((value) => {
console.log(value);
});
Obviously it doesn't work, nothing is ever printed
setTimeout has terrible chaining and error-handling characteristics on its own, so always wrap it:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
function get(c) {
if (c.length) {
return Promise.resolve(c.shift());
}
return wait(1000).then(() => get(c)); // try again
}
let c = [];
get(c).then(val => console.log(val));
wait(2000).then(() => c.push('test'));
While you didn't ask, for the benefit of others, this is a great case where async/await shines:
const wait = ms => new Promise(r => setTimeout(r, ms));
async function get(c) {
while (!c.length) {
await wait(1000);
}
return c.shift();
}
let c = [];
get(c).then(val => console.log(val));
wait(2000).then(() => c.push('test'));
Note how we didn't need Promise.resolve() this time, since async functions do this implicitly.
The problem is that your recursive call doesn't pass the resolve function along, so the else branch can never call resolve.
One way to fix this would be to create a closure inside the promise's callback so that the recursive call will have access to the same resolve variable as the initial call to get.
function get() {
return new Promise((resolve) => {
function loop() {
if (c.length > 0) {
resolve(c.shift());
} else {
setTimeout(loop, 1000);
}
}
loop();
});
}
let c = [];
setTimeout(function() {
c.push('test');
}, 2000);
get().then(val => console.log(val));
In the else case, you never resolve that promise. get might create another one, but it is returned to nowhere.
You should promisify your asynchronous function (setTimeout) on the lowest level, and then only chain your promises. By returning the result of the recursive call from a then callback, the resulting promise will resolve with the same result:
function delayAsync(time) {
return new Promise(resolve => {
setTimeout(resolve, time);
});
}
function get(c) {
if (c.length > 0){
return Promise.resolve(c.shift());
} else {
return delay(1000).then(() => {
return get(c); // try again
});
}
}
What you need is a polling service, which checks periodically for specific condition prior proceeding with promise resolution. Currently when you run setTimeout(get.bind(this), 1000); you are creating a new instance of the promise without actually resolving the initial promise, because you don't reference to the initial resolve function that you created.
Solution:
Create a new callback function that you can reference to it inside the promise
Pass the resolve & reject as params in the setTimeout invocation e.g. setTimeout(HandlePromise, 1000, resolve, reject, param3, param4 ..); setTimeout API
function get() {
var handlerFunction = resolve => {
if (c.length > 0) {
resolve(c.shift());
} else {
setTimeout(handlerFunction, 1000, resolve);
}
};
return new Promise(handlerFunction);
}
let c = [];
setTimeout(function() {
c.push("test");
}, 2000);
get().then(value => {
console.log(value);
});
For more information look into javascript polling article
You could try this solution. Since JS needs to free itself to download the images, I use await within an asynchronous function and an asynchronous call to wake up JS after a delay
private async onBeforeDoingSomething() : void {
await this.delay(1000);
console.log("All images are loaded");
}
private delay (ms : number = 500) : Promise<number> {
return new Promise((resolve,reject) => {
const t = setTimeout( () => this.areImgsLoaded(resolve), ms);
});
}
private async areImgsLoaded (resolve) {
let reload = false;
const img = document.querySelectorAll('img');
console.log("total of images: ",img.length);
for (let i = 0; i < img.length; i++){
if (!img[i]["complete"]) {
console.log("img not load yet");
reload = true;
break;
}
}
if (reload) {
await this.delay();
}
resolve();
}
Use setInterval to check every second. Run this script to understand.
let c = [];
function get(){
return new Promise((resolve) => {
var i = setInterval(function(){
if(c.length > 0){
resolve(c.shift());
clearInterval(i);
}
}, 1000);
});
}
setTimeout(function(){
c.push('test');
}, 2000);
get().then((value) => {
console.log(value);
});

Categories

Resources