I use the following code and there is something that a bit confuse me
if I put in the timeout 1000ms I see that the promise called in the right order
but if I change it to the following
This is step 3
This is step 2
This is step 1
I guess that this happen since when the function is resolved then he proceed to the next function am I correct ? if this is true how can I do this chain that when the first is finished then he proceed to the second etc but without using the promise hell :-)
https://jsfiddle.net/2yym400j/
var step1 = function(ms) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log("This is step 1");
resolve();
}, ms);
})
}
var step2 = function(ms) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log("This is step 2");
resolve();
}, ms);
})
};
var step3 = function(ms) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log("This is step 3");
resolve();
}, ms);
})
};
step1(500)
.then(step2(300))
.then(step3(200))
.catch(function(err) {
console.log(err);
});
Just pass a function instead of the result of the steps.
step1(500)
.then(function() { return step2(300); })
.then(function() { return step3(200); })
.catch(function(err) {
console.log(err);
});
Without this, you are just calling each step without "blocking" for the previous step to resolve.
I know you've already gotten an answer as to why your original code didn't work, but I thought I'd show another way to approach it that is a bit more DRY than what you were doing. You can create a single function that returns a function and then you can use that in the fashion you were using because it returns a function that will be called later which is what .then() wants. So, you can do something like this:
function delay(t, msg) {
return function() {
return new Promise(function(resolve) {
setTimeout(function() {
console.log(msg);
resolve();
}, t);
});
}
}
delay(500, "This is step 1")()
.then(delay(300,"This is step 2"))
.then(delay(200, "This is step 3"))
.catch(function(err) {
console.log(err);
});
Working demo here: https://jsfiddle.net/jfriend00/mbpq4g8m/
Daniel White's comment and answer are correct, but I thought an additional explanation might help.
Your original code broke two rules:
then can't take a Promise, but it can take a function that returns a Promise. If you pass in anything except a function, it will be ignored, just like you passed in null instead.
As soon as you call new Promise(someFunction), the someFunction executes asynchronously without waiting for anything. This means you can call new Promise from within a then function, but if you call it early then it won't have the delayed behavior you're expecting.
So given that each call to stepN returns a newly-constructed promise, I've inlined everything that happens synchronously and renamed the results constructAndReturnPromiseN and constructedPromiseThatStepNReturns. That makes your original code looks like this so all the promises happen at once:
constructedPromise1ThatStep1Returns
.then(constructedPromise2ThatStep2Returns) // BAD: Don't pass promises into then.
.then(constructedPromise3ThatStep3Returns) // BAD: Don't pass promises into then.
.catch(function(err) {
console.log(err);
});
...where Daniel White's code does this:
constructedPromise1ThatStep1Returns
// GOOD: The then function returns a promise that is *constructed inside* the function.
.then(function() { return constructAndReturnPromise2(300); })
.then(function() { return constructAndReturnPromise3(200); })
.catch(function(err) {
console.log(err);
});
Related
I just implemented my first function that returns a promise based on another promise in AngularJS, and it worked. But before I decided to just do it, I spent 2 hours reading and trying to understand the concepts behind promises. I thought if I could write a simple piece of code that simulated how promises worked, I would then be able to conceptually understand it instead of being able to use it without really knowing how it works. I couldn't write that code.
So, could someone please illustrate in vanilla JavaScript how promises work?
A promise is basically an object with two methods. One method is for defining what to do, and one is for telling when to do it. It has to be possible to call the two methods in any order, so the object needs to keep track of which one has been called:
var promise = {
isDone: false,
doneHandler: null,
done: function(f) {
if (this.isDone) {
f();
} else {
this.doneHandler = f;
}
},
callDone: function() {
if (this.doneHandler != null) {
this.doneHandler();
} else {
this.isDone = true;
}
}
};
You can define the action first, then trigger it:
promise.done(function(){ alert('done'); });
promise.callDone();
You can trigger the action first, then define it:
promise.callDone();
promise.done(function(){ alert('done'); });
Demo: http://jsfiddle.net/EvN9P/
When you use a promise in an asynchronous function, the function creates the empty promise, keeps a reference to it, and also returns the reference. The code that handles the asynchronous response will trigger the action in the promise, and the code calling the asynchronous function will define the action.
As either of those can happen in any order, the code calling the asynchronous function can hang on to the promise and define the action any time it wants.
For the simplicity to understand about the promises in Javascript.
You can refer below example. Just copy paste in a new php/html file and run.
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
function test(n){
alert('input:'+n);
var promise = new Promise(function(fulfill, reject) {
/*put your condition here */
if(n) {
fulfill("Inside If! match found");
}
else {
reject(Error("It broke"));
}
});
promise.then(function(result) {
alert(result); // "Inside If! match found"
}, function(err) {
alert(err); // Error: "It broke"
});
}
</script>
</head>
<body>
<input type="button" onclick="test(1);" value="Test"/>
</body>
</html>
Click on Test button,
It will create new promise,
if condition will be true it fulfill the response,
after that promise.then called and based on the fulfill it will print the result.
In case of reject promise.then returns the error message.
Probably the simplest example of promises usage looks like that:
var method1 = (addings = '') => {
return new Promise(resolve => {
console.log('method1' + addings)
resolve(addings + '_adding1');
});
}
var method2 = (addings = '') => {
return new Promise(resolve => {
console.log('method2' + addings)
resolve(addings + '_adding2');
});
}
method1().then(method2).then(method1).then(method2);
// result:
// method1
// method2_adding1
// method1_adding1_adding2
// method2_adding1_adding2_adding1
That's basic of basics. Having it, you can experiment with rejects:
var method1 = (addings = '*') => {
return new Promise((resolve, reject) => {
console.log('method1' + addings)
resolve(addings + '_adding1');
});
}
var method2 = (addings = '*') => {
return new Promise((resolve, reject) => {
console.log('method2' + addings)
reject();
});
}
var errorMethod = () => {
console.log('errorMethod')
}
method1()
.then(method2, errorMethod)
.then(method1, errorMethod)
.then(method2, errorMethod)
.then(method1, errorMethod)
.then(method2, errorMethod);
// result:
// method1*
// method2*_adding1
// errorMethod
// method2*
// errorMethod
// method2*
As we can see, in case of failure error function is fired (which is always the second argument of then) and then next function in chain is fired with no given argument.
For advanced knowledge I invite you here.
please check this simple promise code. this will help you to better understand of promise functionality.
A promise is an object that may produce a single value some time in the future: either a resolved value, or a reason that it’s not resolved. A promise may be in one of 3 possible states: fulfilled, rejected, or pending. Promise users can attach callbacks to handle the fulfilled value or the reason for rejection.
let myPromise = new Promise((resolve, reject)=>{
if(2==2){
resolve("resolved")
}else{
reject("rejected")
}
});
myPromise.then((message)=>{
document.write(`the promise is ${message}`)
}).catch((message)=>{
document.write(`the promise is ${message}`)
})
check this out
I'm trying to implement a timeout for an asynchronous function using a promise and setTimeout. I want to run an asynchronous operation, and if it doesn't complete by a certain amount of time, throw an error. Currently, this is what I have: (some of it found on stackoverflow)
function someOperation(cb) {
setTimeout(function() {
cb(null, 'Done');
}, 2000);
}
var p = new Promise(function(resolve, reject) {
setTimeout(function() {
reject(new Error('Operation timed out'));
}, 100);
someOperation(function(err, res) {
if (err) reject(err);
else resolve(res);
});
});
p.then(function(res) {
console.log(res);
}, function(err) {
console.log("Throwing error...")
throw err;
});
But the program doesn't stop when the error is thrown. Would anybody be able to tell me why, and if there is an easier way to do this? I'd be very much appreciative.
EDIT: Now trying to use bluebird for the first time and it is giving someOperationPromised.timeout is not a function. Am I doing this correctly?
var Promise = require("bluebird");
function someOperation(cb) {
setTimeout(function() {
cb('', 'Done');
}, 2000);
}
var someOperationPromised = Promise.promisify(someOperation);
someOperationPromised.timeout(1).then(function (){
console.log("Finished!");
}).catch(Promise.TimeoutError, function (err) {
throw err;
});
Not sure what promise library you're using, if any. May I suggest Bluebird? It's faster than native and has a lot of great features. Among them? Timeout.
From the documentation:
var Promise = require("bluebird");
var fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync("huge-file.txt").timeout(100).then(function(fileContents) {
}).catch(Promise.TimeoutError, function(e) {
console.log("could not read file within 100ms");
});
Edit:
Hey there! I took some time sorting through this, trying to figure out why your edit didn't work. My goal is to prove to you that .timeout() works. Moreover, I would like to provide you with a workable solution you can use moving forward with Bluebird.
Below I include a function I've named takesFourSeconds. It returns a promise that will resolve after 4 seconds. I then call takesFourSeconds twice. The first time I use timeout to force the promise chain to reject if it takes more than 3 seconds. The second time I force it to reject if it takes longer than 5 seconds.
var Promise = require("bluebird");
function takesFourSeconds (){
return new Promise((resolve, reject) => {
setTimeout(function(){
return resolve('hi');
}, 4000);
});
}
takesFourSeconds().timeout(3000).then(function(res) {
console.log(res);
}).catch(Promise.TimeoutError, function(e) {
console.log("promise took longer than 3 seconds");
});
takesFourSeconds().timeout(5000).then(function(res) {
console.log(res);
}).catch(Promise.TimeoutError, function(e) {
console.log("promise took longer than 5 seconds");
});
Note that this returns:
$ node index.js
promise took longer than 3 seconds
hi
As expected.
Small side note:
When creating a function that returns a promise, you don't have to create a new promise for every function you call in a chain of promises. Only the first function.
For example, if I wanted to call another function after takesFourSeconds() I could write it like this:
function myFunc(result){
return result === 'hi' ? resolve('hey there') : reject('hi there');
}
then:
takesFourSeconds()
.then(myFunc) // myFunc will take in the resolved value of takesFourSeconds implicitly
.then(result => console.log(result)
.catch(error => console.log(error);
This should output:
"hey there"
There you have it! Timeouts in Bluebird.js. :)
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 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.
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));
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);
});
I am really hoping someone can help me with this issue despite my inability (confidentiality issues) to share too much information. Basically, I wrote this function to retry failed promises:
function retryOnFailure(func) {
return func().then(function(resp) {
return resp
}, function(err) {
return setTimeout(function() {
return retryOnFailure(func)
}, 5000)
}
)}
And then I have this code to utilize it:
function getStuff() {
return $.get('/some/endpoint')
}
retryOnFailure(getStuff).then(function(res) { console.log(res) })
Now the recursive retry function works fine, but my then in the last line of code doesn't fire in the event that an error case is reached in retryOnFailure. I know that the reason is because each recursive call creates a new promise so the promise that I'm listening to in my last line is not the same one that gets resolved, but I'm having difficulty figuring out how to remedy this. I want this function to be a generic retry function and as such don't want to handle the actual success in the method, but I'm not seeing any other way. Any help would be greatly appreciated!
return setTimeout(function() {
return retryOnFailure(func)
}, 5000)
looks like you're trying to do the right thing - return the next result from your .catch callback. However, setTimeout does not return a promise, so retryOnFailure() will be resolved immediately (with the timeout cancellation id).
Instead, you need to make sure that for all asynchronous functions that you use a promise is produced, so let's start by promisifying setTimeout:
function delay(ms) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, ms);
});
}
Now we can use that to chain the retry after a delay, and get a promise from it:
function retryOnFailure(func) {
return func().then(null, function(err) {
return delay(5000).then(function() {
return retryOnFailure(func);
});
});
}
Notice that instead of .then(null, …) you can also use .catch(…).
This is the first way that came to mind, there is probably a more elegant solution:
function retryOnFailure(func, onSuccess) {
return func().then(function(resp) {
onSuccess(resp);
}, function(err) {
return setTimeout(function() {
return retryOnFailure(func, onSuccess)
}, 5000)
}
)}
retryOnFailure(getStuff, function(res) { console.log(res) });
The problem is your call to setTimeout, it breaks the flow of your promise. Try something like this instead:
function retryOnFailure(func) {
return new Promise(function(resolve) {
func().then(function(resp) {
resolve(resp);
}).catch(function(err) {
setTimeout(function() {
retryOnFailure(func).then(resolve);
}, 5000)
});
});
};
retryOnFailure(getStuff).then(function(res) { console.log(res) });
Note how I've created a new promise inside the function which never throws an error, but instead will simply attempt to process the func() and recurse itself on failure.
It also may be a good idea to put some kind of retry-limit as well.
Good luck!
I have a promise chain with a recursive promise doAsyncRecursive() in the middle like so:
doAsync().then(function() {
return doAsyncRecursive();
}).then(function() {
return doSomethingElseAsync();
}).then(function(result) {
console.log(result);
}).catch(errorHandler);
doAsyncRecursive() has to do something and if it at first does not succeed, i then after want to try every 5 seconds until it does. This is what my promise function looks like:
function doAsyncRecursive() {
return new Promise(function(resolve, reject) {
//do async thing
if (success) {
resolve();
} else {
return new Promise(function(resolve, reject) {
setTimeout(function() {
doAsyncRecursive();
}, 5000);
});
}
});
}
But when I execute, the chain does not continue after doAsyncRecursive() is successful on the 2nd try and resolve() is called (it continues if the attempt is successful on the 1st try however).
What pattern do I need to make this work?
Catch the failure, wait five seconds, then try again.
function doAsyncRecursive() {
return doAsyncThing().catch(function() {
return Promise.delay(5000).then(doAsyncRecursive);
});
}
Here doAsyncThing is a function corresponding to the //do async thing comment in the OP's code, defined as returning a promise. In the original code, the success or failure of the "do async thing" is tested using a success flag, but by definition asynchronous routines do not deliver such a flag; they deliver their results either via a callback or a promise. The code above assumes that doAsyncThing returns a promise. It also assumes that "failure", in the sense of "does not return the response i want", is represented by that promise rejecting. If instead "success" or "failure" is to be defined as some particular value of a fulfilled promise, then you'd want to do
function doAsyncRecursive() {
return doAsyncThing().then(function(success) {
if (success) return success;
else return Promise.delay(5000).then(doAsyncRecursive);
});
}