How can I delay a chain of promises? I need this because I want to wait for a CSS animation to complete before going on with the script.
The purpose of the function is to open a view. If the view is not already open, then open it (by changing a class), wait for the css animation, go on. If the view is already open, do nothing and go on.
I want to call the function like this: (It is a function within an angular controller)
$scope.openView(viewId).then(function() {
$scope.openAnotherView(anotherViewId);
});
/** Function to open some view **/
$scope.openView = function (viewId) {
function timeout(delay) {
return new Promise(function(resolve, reject) {
$timeout(resolve, delay);
});
}
// Check if view is already open
if ($scope.viewId != viewId) {
$scope.viewId = viewId;
// get data from ajaxcall (also a promise)
return MyService.getData(viewId).then(function(data) {
// add data to view
// change class to open view
// this is working ok!
}).then(function() {
return timeout(30000 /* some large number (testing purpose) */ )
});
} else {
// view is already open, so return here we don't have to wait
// return empty promise, with no timeout
return new Promise(function(resolve, reject) {
resolve()
});
}
}
This code works, but the delay is not working. Is my approach ok? What am I missing here?
Edit 1: improved the code with the suggestion from @sdgluck
Edit 2: Some clarification of the main question:
To clarify the main question a bit more: Can I use this construction in my code?
// code doesnt know wheter to wait or not
// can the Promise do this?
openView().then(function() {
openAnotherView();
}
Outcome 1:
the browser will call openView()
but since it is already open it will just call openAnotherView()
right away (no delay).
Outcome 2 :
The view is not open, so openView()
will open it, then a delay (or as @Dominic Tobias points out, add an eventlister?) then call openAnotherView()
after some delay.
Thanks for any help!
Edit 3: Added a fiddle with the problem explained http://jsfiddle.net/C3TVg/60/
To delay a function call, use setTimeout() function. functionname − The function name for the function to be executed. milliseconds − The number of milliseconds. arg1, arg2, arg3 − These are the arguments passed to the function.
To avoid too many requests, you can fetch data in delay with await and loop and update data using setTimeout() .
setTimeout() is not exactly a perfect tool for the job, but it's easy enough to wrap it into a promise: const awaitTimeout = delay => new Promise(resolve => setTimeout(resolve, delay)); awaitTimeout(300). then(() => console.
How can I delay a chain of promises?
$timeout
returns a promise. Return that promise to chain it.
$scope.openView = function (viewId) {
// Check if view is already open
if ($scope.viewId == viewId) {
//chain right away with empty promise
return $q.when();
};
//otherwise if view is not already open
var p = MyService.getData(viewId).then(function(data) {
// add data to view
// change class to open view
// this is working ok!
});
var pDelayed = p.then (function () {
//return to chain delay
return $timeout(angular.noop, 30000);
});
//return delayed promise for chaining
return pDelayed;
};
$scope.openView(viewId).then(function() {
//use chained promise
$scope.openAnotherView(anotherViewId);
});
To delay a promise, simply call the resolve
function after a wait time.
new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, 3000); // Wait 3s then resolve.
});
The issue with your code is that you are returning a Promise and then inside the then
of that Promise you are creating another one and expecting the original promise to wait for it - I'm afraid that's not how promises work. You would have to do all your waiting inside the promise function and then call resolve:
Edit: This is not true, you can delay the promise chain in any then
:
function promise1() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('promise1');
resolve();
}, 1000);
})
.then(promise2);
}
function promise2() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('promise2');
resolve();
}, 1000);
});
}
function promise3() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('promise3');
resolve();
}, 1000);
});
}
promise1()
.then(promise3)
.then(() => {
console.log('...finished');
})
However that is not a good way to wait for a css animation. It's better to listen to the transitionend event:
element.addEventListener('transitionend', onTransitionEnd);
element.classList.add('transition-me');
Note if you're using an animation
instead of a transition
the same concept applies but use the animationend event.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With