How to delay a promise in when function
Solution 1
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);
});
Solution 2
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.
Solution 3
Each then
accepts a function which should return a Promise. It does not accept an instance of Promise. You want to return the call to timeout
:
return MyService
.getData(viewId)
.then(function(data) {
// ...
})
.then(function () {
return timeout(3000);
});
Alternatively, have timeout
return a function instead of a Promise:
function timeout(delay) {
return function () {
return new Promise(function(resolve, reject) {
// ^^^^^^^ (misspelt in your example)
$timeout(resolve, delay);
});
};
}
And then you can use it as in your example:
return MyService
.getData(viewId)
.then(function(data) {
// ...
})
.then(timeout(3000));
Related videos on Youtube
11mb
Fulltime webmagician, fools around web developing for a living
Updated on June 19, 2022Comments
-
11mb almost 2 years
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 callopenAnotherView()
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 callopenAnotherView()
after some delay.Thanks for any help!
Edit 3: Added a fiddle with the problem explained http://jsfiddle.net/C3TVg/60/
-
Mike Lawson over 8 yearsI think you may be going about this the wrong way. It sounds like you might want to detect if the animation is completed like this instead.
-
-
11mb over 8 yearsThx for identifying my mistake with the timout function and clearing up how to resolve it. But I think that there is another issue with my code like @Dominic Tobias is pointing out.
-
sdgluck over 8 years@11mb No problem. And yes, the other answer is more comprehensive. :-)
-
11mb over 8 yearsThx for your answer, I just updated my question. I tried to clarify the fact that I don't now on beforehand if I have to wait.
-
georgeawg over 8 yearsUpdated to add conditional delay.