I have a series of async functions that i want to execute in a sequence, i tryed using promises but the exmple i followed tho it works, does not execute then in orther, and many times one resolves the content, before the other draws the container. i have tryed to understand the promises thing and it seems that the promises is being rejected, but i dont know what im doing wrong, or if i should use other method to "chaing" the functions. Thanks for the help!
var chain = new Promise(function(){
statusUp();
});
chain.then(p_view(pid)).then(pu_list(pid)).then(pm_list(pid));
You're looking for something similar to this
Promise.resolve(statusUp())
.then(function(pid) {
return p_view(pid)
})
.then(function(pid) {
return pu_list(pid)
})
.then(function(pid) {
return pm_list(pid)
});
I made some (a lot of...) assumptions here regarding statusUp, p_view, pu_list and pm_list so this might need some tweaking.
Related
I used the code below for years when I want to open a window and check when an specific element becomes present in the DOM of the opened window. It works fine, never had a problem. PS: assume jQuery is already imported into this project.
openedWindow = window.open("http://localhost/teste1.php");
myClock = window.setInterval(
function() {
if ($(openedWindow.window.document).find("#myElement").length == 1) {
window.clearInterval(openedWindow);
//DO MY STUFF HERE.
}
},
100
);
So yesterday a friend of mine came to me, saw this code and said "You should do that with promises". He said he didnt know how promises worked in depth but he said my code would get smaller and easier to read.
Ok. I dont know promises too. So I studied for over an hour and came up with the code below that works "fine". Except it's a lot bigger and I cant find it easier to read at all.
new Promise(function(resolve,reject) {
openedWindow = window.open("http://localhost/teste1.php");
window.setInterval(
function() {
window.clearInterval(openedWindow);
if ($(openedWindow.window.document).find("#myElement").length == 1) {
resolve(true);
}
},
100
);
}).then(
function(result) {
if (result) {
//DO MY STUFF HERE.
}
}
);
So was my friend wrong? Or I am the wrong one doing the wrong thing with prommises? Is there a better way to do that with promises that I, a newbie in this subject, and not seeing?
Disclamer:
I will leave my answer since I believe it is useful in the context of understanding Promises. The question is related to wait for stuff in the DOM, but I believe OP's main concern is to understand why one would use a Promise over a callback.
However, said so, for the specific problem related to react to DOM changes, Nino's answer is the best.
Original answer
Except it's a lot bigger and I cant find it easier to read at all.
Well, the idea I think is that:
You expose a function like waitForElement which takes an element and returns a Promise that resolves when the element is found.
Now any part of your code can use this function to get a Promise of element being found. This is important because you can attach callbacks to already resolved Promises and still get it run (this is important if you're using a Promise that waits for a database connection, for instance).
So your function would be something like:
function waitForElement(element){
return new Promise(function(resolve) {
// maybe uri could be a param too
openedWindow = window.open("http://localhost/teste1.php");
window.setInterval(
function() {
window.clearInterval(openedWindow);
if ($(openedWindow.window.document).find(element).length == 1) {
resolve(true);
}
},
100
);
});
}
Note the return at the beginning of the method. So now any part of your code could call:
waitForElement('#foo').then( // do my stuff //);
This is still pretty much the same, as you say. However, a good thing about promises is that them allow to attach callbacks, and they cache the async operations, so now you can:
const fooIsPresent = waitForElement('#foo');
// later in your code
fooIsPresent( // some stuff //);
// and even later, maybe minutes later:
fooIsPresent(// still more stuff //);
Now, if the element still is not present, the stuff callbacks will be invoked when the moment arrives.
However, if the element was already found, and you call fooIsPresent after that, the callback will be executed immediately (in the next tick).
And if you want some stuff to always happen before another, you can chain them:
fooIsPresent.then(// first stuff //)
.then(// second stuff //);
So yes, the code is bigger and maybe a bit less clear, but now is more useful and you can use it in some handy ways.
You're both wrong but you're a bit more wrong than him because writing this code as promise allow you to compartiment your code better:
/* without promises */
// code for waiting for an element to appear
// code for after
// code for waiting for an element to appear
/* with promises */
// code for waiting for an element to appear
// code for after
But yeah, you're both wrong because the modern way to wait for an element to appear is to use mutation observers:
const observer = new MutationObserver(mutationRecordList => {
for (mutationRecord of mutationRecordList) {
if (mutationRecord.target.getAttribute('id')=='#my-element') {
console.log('The element just appeared');
}
}
});
observer.observe(document.body, {childList: true, subtree: true});
and wrap the whole thing as a promise, depending on your code.
I agree with your assessment; your Promises version is longer and adds no clarity. Some people think that Promises make all code better, which is just untrue. You can have well-written or poorly-written code, irrespective of whether or not you use Promises. I personally find that clarity is often lost with Promises.
In the first example, I think you meant to pass myClock to clearInterval(). Anyway, I prefer to use setTimeout when I think it will probably be canceled shortly.
I like having a self-contained structure.
(function checker(openedWindow) {
if (openedWindow.window.document.querySelector("#myElement")) {
doMyStuff(openedWindow);
} else {
setTimeout(checker, 100, openedWindow);
}
})(window.open("http://localhost/teste1.php"));
function doMyStuff(openedWindow) {
// DO MY STUFF
}
The Problem:
In Protractor, expect() is patched to implicitly understand promises which enables a shorthand assertion style. E.g.:
expect(elm.getText()).toEqual("expected text");
elm.getText() here does not need to be explicitly resolved with then() and would be implicitly resolved by Protractor before the expectation is checked.
But, what if the "to equal" part is also a promise. For instance, a text from an another element. In this case we have to resolve the second part explicitly:
elm2.getText().then(function (text2) {
expect(elm1.getText()).toEqual(text2);
});
The Question:
Is it possible to patch Jasmine/Protractor to make it understand promises on both sides of the assertion? To be able to write:
expect(elm1.getText()).toEqual(elm2.getText());
Just tested with promises for both sides - and it resolves them OK.
Try at your project. Maybe you have nothing to do:
describe('ololo', function () {
it('both sides are promises', function () {
browser.get('http://www.protractortest.org/testapp/ng1/#/form');
let elementText1 = $('.ng-scope p').getText();
let elementText2 = $('#transformedtext>h4').getText();
//Will fail here, but you can see that it successfully resolved promises
expect(elementText1).toEqual(elementText2);
});
});
If this does not work for you - i think you can use protractor.promise.all, just example:
protractor.promise.all([elm2.getText(), elm1.getText()])
.then(texts=> expect(texts[0]).toEqual(texts[1]), 'texts should be same')
Or harder way - create own matchers. See how i work with promises inside matcher in my lib:
https://github.com/Xotabu4/jasmine-protractor-matchers/blob/master/index.js#L39
Not pretty, but you could resolve the param. It's a no-op for non promises...
expect(elm1.getText()).toEqual(Promise.resolve(elm2.getText()));
I really struggle with that... I have a function in my controller generating a certain amount of promises, passed to another function which also generate promises...
angular.forEach($scope.items,function(value,key) {
myService.getPage(item).then(function(data) {
myFunction(data)
});
function myFunction(singlePage) {
myService.getPage(singlePage.next).then(function(data) {
myFunction(data)
});
}
What i want to achieve is to somehow know when all promises will be resolved
I tried to wrap my promises in an array and pass it to $q.all in all kind of configuration but without success..Any kind of help would be really cool !! Thanks
Ignoring the infinite recursion loop you've created, let's do this...
var allPromises = [];
angular.forEach($scope.items,function(value,key) {
allPromises.push(myService.getPage(value));
});
$q.all(allPromises)
.then(function (arrayOfResults) {
// do something
});
One thing to note, you are passing in value and key, but using "item". You need to be using value.
I feel this is a very bad idea as i do not find it anywhere but testing it on my localhost it seems to work. I have a service :
angular
.module('trips.services')
.service('Trip', Trip);
function Trip($http, $q) {
//.....
function create(data) {
var api_url = "/api/trip";
return $http.post(api_url, data).success(function(data) {
mixpanel.track("Trip: created", {}); // track that a new event happened
});
}
}
I also have a controller where i do something like:
Trip.create(data).success(function(data) {
//do something after the trip is created
})
Now as you can see in this example I use both the then promise and the success callback in the service. If i do breakpoints in the debugger the success callback from the service is executed first and then the then clause from the controller. And it is also great for code organization, the common things i do after the service is successful is in the service while the specific things are in the controller. The thing is, i feel it is very non-according to any documentation or examples. Did not really find it somewhere, I discovered it by mistake. It might not work in some cases also?
I know the other options also, the $q library and could deffer in the service but that ends up in much more code.
So, is this a bad idea and why? Any opinions would be very helpful. I do not want to set up a wrong practice.
Using promises consequently is considered a good practice as you already know. To do that, you can simply replace your success-callback with another then:
function create(data) {
var api_url = "/api/trip";
var promise = $http.post(api_url, data);
promise.then(function(data) {
mixpanel.track("Trip: created", {}); // track that a new event happened
});
return promise;
}
Having multiple independent .then()s on one promise is perfectly fine and works as you would expect.
It's not necessarily a bad idea, it's just you using chaining of promises.
The .then function looks like this:
.then(function(success){}, function(error){});
you can see that .success is just shorthand for the first function above (as if you didn't declare the error callback).
Seeing as the promise can be passed around or maybe you have multiple services making the same request and you want just one promise created, then you might like to declare callbacks from multiple areas yet have just one promise resolve.
So is it a bad idea? No. Why? It is a flexible piece of code which is really up to the programmer to use. Can you declare multiple thens? Yes. Should you mix it with success callbacks? You can, but you might just want to stick to one style for consistency. But thats really up to what you're trying to achieve.
Angular documentation: https://docs.angularjs.org/api/ng/service/$http
(Watch out for deprecation, might just want to stick to .then)
Ive been trying to find the simplest JavaScript promise(without libraries) so i can get it into my thick skull. Thought i was getting the concept, but seem to be unable to work it. Can anyone fix this?
function fetch(message){
return message;
}
var promise = fetch('beep')
promise.then(function(dMessage){
console.log(dMessage);
}).catch(function(err){
console.error('error-', err);
})
ERROR Message:promise.then is not a function
Am also a little confused about when i can use this? NodeJs will be able to use this without extra libraries right? for client side i should get ES6-promise?
Solution underneath with thanks to Daniel B
function fetch(message){
return new Promise(function(resolve, reject){
resolve(message);
});
}
fetch('word').then(function(dMess){
console.log(dMess);
}).catch(function(err){
console.error('error-', err);
})
You are overwriting the native fetch function. Simply remove your function and it will work.
If you want to define your own fetch function, it has to return a promise, as below.
function fetch(message){
return Promise.resolve(message);
}
I don't believe the other answer is correct. Although it's correct in 'fetch' is a method native to browsers (not javascript which the user implied) it is not the reason why your promise doesn't work, and doesn't explain what you're not understanding about promises.
The first 'promise' in a chain always has to be a promise constructor/instance. You can create these in many ways, but if you have only a simple value like a string, you must wrap it in Promise.resolve(string). Afterwords, and other promises in the chain won't need this functionality as they are all wrapped in the top level promise of the chain. At least this is how most implementations like Q/Bluebird/ES6 Native promises will work.