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
Related
I was trying to implement promise from scratch.
Question:
I wasn't sure how do I implement Finally? (I'm guessing finally will
execute after then's and the catch is invoked. (ONLY once))
If you feel any refactors can be made to my code, please feel free to suggest. This is my naive attempt to implement promises.
This is my implementation:
function Promisify(fn) {
let status = 0; // 0 = unfulfilled, 1 = resolved, 2 = rejected
let result;
let error;
let thenFns = [];
let catchFns = [];
let finallyFn = undefined;
// Public Methods.
this.then = function(fn) {
thenFns.push(fn);
doThen();
return this; // for chaining
};
this.catch = function(fn) {
catchFns.push(fn);
doCatch();
return this; // for chaining
};
// TODO: Implement finally
this.finally = function(fn) {
finallyFn = fn;
// dofinally(fn);
return this;
}
// Private Methods
function resolve(r) {
if (status) throw Error('can not resolve, already handled');
status = 1;
result = r;
doThen();
}
function reject(e) {
if (status) throw Error('can not reject, already handled');
status = 2;
error = e;
doCatch();
}
function doThen() {
if (status === 1) {
while(thenFns.length) {
thenFns.shift()(result);
}
}
}
function doCatch() {
if (status === 2) {
if (catchFns.length === 0) {
console.error('uncaught error')
}
while(catchFns.length) {
catchFns.shift()(error);
}
}
}
try {
fn(resolve, reject);
} catch (e) {
reject(e);
}
}
// ======== QUESTION: Caller ================
const demoFail = new Promisify((resolve, reject) => {
setTimeout(function() {
reject('Howdy! from DemoFail')
}, 1000);
});
demoFail
.then(val => console.log("DemoFail Then!!"))
.catch(console.error) //Should throw an error.
.then(val => console.log("Second then!"))
.catch(
(err) => {
throw new Error('error', err);
})
.finally(val => console.log("Executed Finally"))
Here are some things missing from your implementation:
.then() needs to return a new promise, not the same promise.
The return value of a .then() handlers (if it's not a promise) becomes the resolved value of the newly returned promise returned in step #1.
If the return value of a .then() handler is a promise, then the newly returned promise from step 1 doesn't resolve until the promise returned from the .then() handler resolves or rejects with that value.
When calling a .then() handler, you need try/catch around the call to the handler because if the handler throws, then that turns the promise returned into step #1 into a rejected promise with the throw error as the reject reason.
There is similar logic for all the .catch() handlers (it also returns a new promise and the return value of the handler affects the newly returned promise from step #1).
When you store .then() or .catch() callback fns, you also need to store the newly create promise that you returned separate for each callback because that promise will be affected by the return value or exception thrown in the callback.
The spec itself that covers this is fairly simple. You can read it here. And, here's an implementation of that spec.
Your simple version looks like it probably works for one level of promise like fn().then(), but it doesn't do proper chaining and doesn't catch exceptions in handlers and doesn't pay attention to return values in handlers which are all pretty fundamental behaviors of promises. Unfortunately, there is no super simple way to write promises that includes the fundamental behaviors. It just takes more code to support them.
Coming from a heavy background in PHP I am struggling with some aspects of node/js.
const ldap = require('ldapjs');
class LdapClient {
constructor({
url,
}) {
this.isBound = null;
this.client = ldap.createClient({ url });
}
authenticate(credentials) {
const _this = this;
return new Promise((resolve, reject) => {
return this.client.bind(credentials.username, credentials.password, (err, res) => {
if (err) {
this.client.unbind();
return reject(err);
}
_this.isBound = true;
return resolve(res);
});
});
}
}
const client = new Client({url: ''})
const credentials = {
'username': '',
'password': ''
}
client.authenticate(credentials)
.then(() => {
console.log('authenticated');
console.log('race = ' + client.isBound); // SHOWS TRUE
})
.catch(e => {
console.log(e);
})
console.log(client.isBound); // SHOWS NULL... WANT TRUE (RACE ISSUE as consoles before PROMISE)
I am trying to access the isBound property outside of the promise return where it is set to true inside the authentication method on success.
However as you can see there appears to be a possible race condition?
Is there a way to handle this...
Thanks
It is not a race condition. It's working fine as expected. There are two console.logs in your code. The first one is in promise and the other one is outside the promise.
Your call goes into asynchronous mode, and the last console.log get executed sequentially as the next command in order, which at that time, the value of the variable was null. Your variable resolves later with the correct value.
If you have to perform further actions, you have to do it in the .then() portion of your Client method which will only execute when your Promise has resolved.
For example
Client().then() {//all of your post response related logic should be here}
So you're misunderstanding something about promises. They're meant to be used for Asynchronous code, like so:
let p = new Promise(resolve => setTimeout(resolve, 1000, 'here'))
p.then(console.log)
//in one second 'here'
As you can see the then doesn't actually happen until AFTER the promise resolves. With asynchronous code that's whenever resolve gets called.
So what's happening in your code is as follows:
Create Promise -> set event loop callback
console.log(isBound) // false, but happens first because it's called sync
Promise resolves // true
so really in your promise resolution is the first place you're even going to be able to check it successfully. If you return it from the call you can chain there and make sure the scope is continued later.
let a = 0
let p = Promise.resolve(a)
.then(a =>{
a += 2;
return a;
})
.then(a => console.log(a) || a)
console.log(a) // 0
p == p.then(a =>{
a += 4;
return a;
})
.then(console.log) // false because promises aren't equal and .then/.catch return new promise chains
// 2
// 6
The 2,6 and the false comparison may print out of order because of the event loop, however if you keep it all in the same lexical scope then you'll still have access to a or this within the confines of your promise chain.
Side note: You don't need to reference _this versus this with arrow function inside class methods. They will lexically scope and thus this will be bound to the local scope of that function. More information can be found at You Don't know JS
You're trying to set isBound when the promise is created, not when it's resolved.
Rather than returning the promise directly from the authenticate() method, you can store it in a variable, call .then() on it, and return the promise chain at that point.
authenticate(credentials) {
// create your promise and store it
let authPromise = new Promise((resolve, reject) => {
...
})
// handle the promise and set isBound before returning the chain
return authPromise.then(res => {
_this.isBound = true
return res
})
}
This can be written with fewer lines, but this is meant to illustrate promise chaining and interception before returning.
ADDITIONALLY Your final console.log() is outside of your promise handler (a .then()) so it's always going to be null since that code gets run synchronously, before the authenticate async function has time to complete.
client.authenticate(credentials)
.then(res => {
// you MUST do all your async-dependant operations inside
// promise handlers like this
console.log(client.isBound);
})
This is tightly coupled to Chaining .then() calls in ES6 promises ...
I tried this with some functions that make up a chain of promises, so basically:
var PromiseGeneratingMethod = function(){
return p = new Promise((resolve, reject) =>{
resolve(1)
});
}
var inBetweenMethod = function(){
return PromiseGeneratingMethod()
.then((resolved) => {
if(resolved){
console.log('resolved in between');
//return resolved
/* this changes output to
resolved in between
resolved at last*/
}else{
console.log('something went terribly wrong in betweeen', resolved);
}
});
}
inBetweenMethod().then((resolved) =>{
if(resolved){
console.log('resolved at last')
}else{
console.log('something went terribly wrong', resolved);
}
})
/* ouput:
resolved in between
something went terribly wrong undefined*/
I don't understand why it is like that. doesn't have a Promise just ONE associated return value? why can I change that value in every then? It seems irrational to me. A Promise Object can only have one return value and I thought every then handler will receive the same parameter after the Promise gets resolved?
This way, having two Methods which call then() on the same Promise, the latter one (in asynchronous environments you never know what that is...) will ALWAYS get an empty result, except if EVERY then returns the desired value
If I got it right, the only good thing is that you can build a then().then().then() chain to make it almost synchronous (by returning arbitrary values in every then()) but you still could achieve the same with nested Promises, right?
Can someone help me understand why es6 Promises work that way and if there are more caveats to using those?
doesn't have a promise just ONE associated return value?
Yes.
why can I change that value in every then?
Because every .then() call does return a new promise.
having two methods which call then() on the same Promise
That's not what you're doing. Your then callbacks are installed on different promises, that's why they get different values.
You could do
function inBetweenMethod() {
var promise = PromiseGeneratingMethod();
promise.then(resolved => { … }); // return value is ignored
return promise;
}
but you should really avoid that. You already noticed that you can get the expected behaviour with
function inBetweenMethod() {
var promise = PromiseGeneratingMethod();
var newPromise = promise.then(value => {
…
return value;
});
return newPromise;
}
where the newPromise is resolved with the value that is returned by the callback - possibly the same value that promise fulfilled with.
you are using .then() handler twice, do the following:
var PromiseGeneratingMethod = function(){
return new Promise((resolve, reject) =>{
if (myCondition) resolve(1)
if (!myCondition) reject("failed")
});
}
var inBetweenMethod = function(){
return PromiseGeneratingMethod()
}
inBetweenMethod().then((resolved) =>{
console.log(resolved)
}).catch(function(err) {
console.log(err)
})
I am looking to fire a series of functions after a few other functions have run and loaded content to my webpage. I have used setTimeOuts but I understand it's poor practice given there could be a lag in the connection, in which case they would run incorrectly. How do I do functions sequentially without using setTimeOuts.
The avgEmotion and avgSentiment functions are supposed to run once a series of lists are created on the webpage through the newsApiLeft() and newsApiRight() functions.
Thanks so much in advance.
button.addEventListener("click", function () {
var filter = document.getElementById("news_cat");
var filterSource = document.getElementById("news_source");
var category = filter.value;
var source = filterSource.value;
params.sources = source;
params.q = category;
parameters.q = category;
var filterL = document.getElementById("news_sourceL");
var sourceL = filterL.value;
parameters.sources = sourceL;
showContent();
clearTextRight();
clearTextLeft();
newsApiLeft();
newsApiRight();
setTimeout(avgEmotionR, 2000);
setTimeout(avgEmotionL, 2000);
setTimeout(avgSentiment, 2000);
})
The right way to execute a function after another one returns (asynchronous calls) is by using Promises. Make your newsApiLeft() and newsApiRight functions returning a Promise and then call them like follows:
var newsApiLeft = new Promise(function(resolve, reject) {
// do something long running
let everythingWasOK = true;
if (everythingWasOK) {
resolve("I can return a value");
//or just resolve();
} else {
reject(Error("Or error"));
}
});
newsApiLeft.then((returnedData)=>{
avgEmotionL();
//run another function here;
//you can use the returnedData
}, (error)=>{
//optionally handle error
})
You can achieve this using JavaScript promises.
Here i created a Promise A, where I do my logic processing at the comment mentioned below. Then you need to either resolve or reject it based on your logic. If you resolve then() function will be called.
In the first then() function you can nest new promise. Like wise you can nest as many as you want. This way you can make sure promise B will execute only after end of promise A.
var A = new Promise(function(resolve, reject) {
// Do an async task here and then...
if(/* good condition */) {
resolve('Success!');
}
else {
reject('Failure!');
}
});
A.then(function() {
/* do something with the result */
}).catch(function() {
/* error :( */
})
For executing promise A just do A();
You could achieve this using callbacks:
function newsApiLeft(callback) {
// Load stuff
// Then call the callback; the code you want
// to run after the loading is complete
callback();
}
newsApiLeft(function() {
avgEmotion();
avgSentiment();
});
If you're using the same callback for both Left and Right, you could save the callback and use it for both:
var callback = function() {
avgEmotion();
avgSentiment();
}
newsApiLeft(callback);
newsApiRight(callback);
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);
});