Chaining observable and promise - javascript

I am trying to update user's profile picture with AngularFire. I use the put method which returns a promise. Inside my promise I use an observable to change the image URL in user's info. At the end I change the image URL of the picture's DOM element to the image URL in user's info. The problem is that my promise tries to get the user's image URL before my observable is done updating it. Is there a way to chain an observable to promise so that promise only continues if observable is complete?
Here is my code:
authService.ts
updatePicture(profilePicture){
//first put the pictuer in the storage
return storageRef.put(profilePicture)
.then(snapshot => {
downloadURL = snapshot.downloadURL;
})
//this is where I use the observable to update user info
.then(() => {
this.getUserAuth().subscribe(userAuth => {
userAuth.updateProfile({
displayName:userAuth.displayName,
photoURL:downloadURL
})
})
})
}
user.component.ts
// I want this "then" to only happen when my observable is complete.
authService.updatePicture().then(p => {
document.querySelector('img').src = this.userAuth.photoURL);
}

Try wrapping the observable into a new promise, and use that in your chain of promises like this:
var obs = new Observable(...);
var promise = new Promise((resolve, reject) => {
obs.subscribe(your_on_next, reject, resolve);
});
This should give you a promise that will resolve when the observer terminates. Now you can use that promise in the chain of promises at the point you require. Simply return it from another then() in that chain.
You'll need to add some extra code to get anything out of that observable that you'd need downstream in the chain.

Related

Using Promise inside a Observable

Since inside a Observable we have an option of calling Promise ,I have a clarification regrading this.
Since a promise gets executed immediately after declaration , will it be executed without attaching Subscribe method to it ?
And also since it cannot be cancelled why would anyone think of calling a Promise inside a observable .
Rx.Observable.fromPromise():
const computeFutureValue = new Promise((resolve, reject) => {
//make http api call
});
Rx.Observable.fromPromise(computeFutureValue)
.subscribe(
val => {
console.log(val);
},
err => {
console.log(`Error occurred: ${err}`);
},
() => {
console.log('All done!');
});
As you said, a Promises body is executed when you create the Promise.
So when you create this Promise:
const computeFutureValue = new Promise((resolve, reject) => {
//make http api call
});
the http request is executed no matter what you do next. Using from (or fromPromise) to convert the Promise to an Observable and subscribing to this Observable doesn't affect the Promise and it's execution in any way.
You'd want to convert a Promise to an Observable if you want to be able to work with all the operators Observables offer or because your app works with and requires Observables at certain points.
If you only want to create and execute a Promise when you subscribe to an Observable you can use defer, see my answer here: Convert Promise to Observable

Get a promise that resolves with the next value emitted

I'm trying to resolve a promise with the first value emitted since subscribing to an Observable.
I first tried using .toPromise():
await observable.toPromise()
but that only works when observer.complete() is called within the observable.
take(1) and first() also aren't suitable because they just allow the values to be piped to other observables.
At the moment, I've come up with this code:
await new Promise(resolve => {
const subscription = observable.subscribe(data => {
resolve(data)
subscription.unsubscribe()
})
})
Is there a utility function that I'm not using or is there a way to simplify it further?
You need to use the first operator to have the observable emit the first value and complete.
await observable.pipe(first()).toPromise()

Why does a Promise return an object Promise, even if I put `return` explicitly? [duplicate]

This question already has answers here:
JS: Promise doesn't return value
(2 answers)
Closed 3 years ago.
I expected a Promise will return a value that I put.
But It doesn't return a value but returns Promise { pending }
var c = new Promise((resolve, reject)=>{
console.log('here');
return 'return here';
// resolve('resolve here')
});
console.log(c);
I expected there will be return here instead of Promise { pending }
Why does a Promise return an object Promise, even if I put return explicitly?
Well, because that's not how promises are designed. The return value from the promise executor callback function is not used at all.
If you want to know why it wasn't designed the way you want it to be designed, you'd have to go find the actual engineers who were involved in the early design of the Promise constructor and its executor function.
Relevant to us is "how it works", not really why one design path was selected vs another. var c = new Promise(...) always returns a newly created promise just like most would expect it to.
And, the promise executor function is used to allow you to start your asynchronous operation and then eventually track its completion or error with resolve() or reject(). There are no code paths where you instead decided you're going to return a value so that new Promise() should just return that value instead of returning a new promise. So, as most would expect new Promise() always returns a promise, regardless of what you do in the promise executor callback function.
As you appear to already know, if you want to immediately set the resolved value of the promise, then you call resolve(someValue) and that sets the resolved value of the promise.
You get values out of a promise with .then() or with await. Those are the only two ways.
I expected there will be return here instead of Promise { pending }
First off, you have to realize that the promise executor callback function is a function you provided the new Promise() constructor. That constructor creates a new promise object, creates unique resolve and reject functions and passes them as arguments to that callback function you provided and it calls that function. It doesn't pay any attention to the return value from that function. The designers of this executor function and the whole promise initialization process decided that the return value of that function was irrelevant. There are two ways to change the state of the new promise, by calling resolve(someValue) or reject(someReason). Only those two ways.
Second off, understand that the return in that function is just returning back from the callback function you provided and the flow of execution finishes up in the Promise constructor and then it returns from the new Promise() constructor and assigns that new promise to your variable c. A new Promise has been created at that point. If the promise executor did not call resolve() or reject() yet that new promise will be in the pending state. If resolve() or reject() was called immediately, then the promise may be fulfilled or rejected (the other two states it can be in).
There is no scenario where new Promise() returns your value.
In your specific code (with the call to resolve() put back in), you can get the value from the promise either like this with.then()`:
let c = new Promise((resolve, reject)=>{
console.log('here');
resolve('resolve here');
}).then(val => {
console.log(val);
});
Or using await (inside an async function):
async function someFunction() {
let c = new Promise((resolve, reject)=>{
console.log('here');
resolve('resolve here');
});
let val = await c;
console.log(val);
}
This is not how promises work. The point of the promise is to control the execution flow. You will not get a value until the promise is resolved:
var c = new Promise((resolve, reject)=>{
console.log('here');
// return 'return here';
resolve('resolve here')
});
c.then(result => {
console.log(result)
})
cause its not resolved yet, resolve or reject is the end value
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
A promise represents an asynchronous computation, so you can't access its result synchronously, you need to chain a callback with .then(), see the doc here.
Here is a working example:
const c = new Promise((resolve, reject)=>{
resolve('resolve here')
});
c.then(value => console.log(value));
That's not the correct way of using Promises. You will have to resolve promise and use then method to see resolved promise.
You will have to do as:
var c = new Promise((resolve, reject)=>{
console.log('here');
//return 'return here';
resolve('resolve here')
});
c.then(function(value) {
console.log(value);
});
You should use resolve instead of return. Above has answered your questions perfectly.
const c = new Promise((resolve, reject)=>{
console.log('here');
resolve('resolve here')
});
c().then((answer) => console.log(answer))

Returning a promise inside another not working [duplicate]

I want to fulfill a promise with some other promise. The point is that I really want to get access to the (still pending) second promise as soon as the first promise is fulfilled. Unfortunately, I only seem to be able to get the second promise's resolution value once both both promises are fulfilled.
Here's the use case that I have in mind:
var picker = pickFile();
picker.then( // Wait for the user to pick a file.
function(downloadProgress) {
// The user picked a file. The file may not be available just yet (e.g.,
// if it has to be downloaded over the network) but we can already ask
// the user some more questions while the file is being obtained in the
// background.
...do some more user interaction...
return downloadProgress;
}
).then( // Wait for the download (if any) to complete.
function(file) {
// Do something with the file.
}
)
The function pickFile displays a file picker where the user may pick a file either from their own hard drive or from a URL. It returns a promise picker that is fulfilled as soon as the user has picked a file. At this point, we may still have to download the selected file over the network. Therefore, I cannot fulfill picker with the selected file as resolution value. Instead, picker should be fulfilled with another promise, downloadProgress, which in turn will eventually be fulfilled with the selected file.
For completenes, here's a mock implementation of the pickFile function:
function pickFile() {
...display the file picker...
var resolveP1 = null;
var p1 = new Promise(
function(resolve, reject) {
resolveP1 = resolve;
}
);
// Mock code to pretend the user picked a file
window.setTimeout(function() {
var p2 = Promise.resolve('thefile');
resolveP1(p2); // <--- PROBLEM: I actually want to *fulfill* p1 with p2
}, 3000);
return p1;
}
The problem in the marked line is that I would like to fulfill the promise p1 with the new promise p2, but I only know how to resolve it. The difference between fulfilling and resolving is that resolving first checks if the supplied value p2 is again a promise. If it is, then fulfillment of p1 will be deferred until p2 is fulfilld, and then p1 will be fulfilled with p2's resolution value instead of p2 itself.
I could work around this issue by building a wrapper around p2, i.e. by replacing the line
resolveP1(p2); // <--- PROBLEM: I actually want to *fulfill* p1 with p2
from the second code example by
resolveP1({promise: p2});
Then, in the first code example, I'd have to replace the line
return downloadProgress;
by
return downloadProgress.promise;
But this seems like a bit of a hack when all I really want to do is just fulfill (instead of resolve) a promise.
I'd appreciate any suggestions.
There doesn't seem to be a solution apart from the workaround I already described in the question. For future reference, if you want to fulfill (rather than resolve) a promise p with a value val, where val is another promise, then just calling the promise resolution function for p with argument val won't work as expected. It would cause p to be "locked in" on the state of val, such that p will be fulfilled with val's resolution value once val is fulfilled (see spec).
Instead, wrap val in another object and resolve p with that object:
var resolveP; // Promise resolution function for p
var p = new Promise(
function(resolve, reject) {
resolveP = resolve;
}
);
function fulfillPwithPromise(val) { // Fulfills p with a promise val
resolveP({promise: val});
}
p.then(function(res) {
// Do something as soon as p is fulfilled...
return res.promise;
}).then(function(res) {
// Do something as soon as the second promise is fulfilled...
});
This solution works if you already know that val is a promise. If you cannot make any assumptions about val's type, then you seem to be out of luck. Either you have to always wrap promise resolution values in another object, or you can try to detect whether val has a field then of type "function" and wrap it conditionally.
That said, in some cases the default behavior of promise resolution may actually have the desired effect. So only use the workaround described above if you are sure that you want to fulfill instead of resolve the first promise with the second one.
Although different people use different terms, in common terminology, "fulfill" means to put a promise in the "success" state (as opposed to "reject")--the state that will trigger then then handlers hanging off it.
In other words, you cannot "fulfill" a promise with a promise. You can fulfill it with a value. (By the way, the term "resolve" is usually meant as either of fulfilling or rejecting.)
What you can do is return a promise from a .then handler and that will have the effect of essentially replacing the original promise with the returned promise.
Here is a simple example of doing that:
asyncTask1 . then(asyncTask2) . then(processData)
where asyncTask1 is a promise, and asyncTask2 is a function which returns a promise. So when asyncTask1 is fulfilled (done successfully), then asyncTask2 runs, and the promise returned by the .then is "taken over" by the promise asyncTask2 returns, so that when it finishes, the data can be processed.
I can do something similar by calling Promise.resolve with a promise as parameter. It's a bit of a misnomer, because I'm not resolving the promise in the technical sense. Instead, the new promise created is "inhabited" by the promise I passed in. It's also useless, because using the result is exactly the same as using the promise I passed in:
Promise.resolve(asyncTask2)
behaves exactly the same as
asyncTask2
(assuming asyncTask2 is already a promise; otherwise Promise.resolve has the effect of creating a promise which is immediately fulfilled with the passed in value.)
Just as you can pass a promise to Promise.resolve, you can pass a promise to the resolve function provided to you as a parameter of the promise constructor callback. If the parameter you pass to resolve is a non-promise, the promise immediately fulfills with that value. However, if the parameter you pass to resolve is another promise, that promise "takes over the body" of the promise you are constructing. To put it another way, the promise you are constructing starts to behave exactly as the the promise passed to resolve.
By "behave exactly" I mean, if the promise you pass in to resolve is already fulfilled, the promise you are constructing is instantly fulfilled with the same value. If the promise you pass in to resolve is already rejected, the promise you are constructing is instantly rejected with the same reason. If the promise you pass in to resolve is not resolved yet, then any then handlers you hang off the promise you are constructing will be invoked if and when the promise you pass to resolve is resolved.
Just as it is confusing that Promise.resolve may result in a promise which is not actually resolved, it is similarly confusing that calling the resolve function handed to you as a parameter to the promise constructor may not actually resolve the promise being constructed if you call it with an unresolved promise. Instead, as I've said a couple of times now, it has the effect of putting the promise being constructed in a state of total congruence with the promise passed to resolve.
Therefore, unless I am missing the point of your question, pickfile could be written as
function pickFile() {
return new Promise(function(resolve, reject) {
...display the file picker...
// Mock code to pretend the user picked a file
window.setTimeout(function() {
resolve('thefile');
});
}
I didn't really understand your question clearly, so this might not be what you want. Please clarify if you care to.
Found a similar solution in the process of moving away from Angular's $q to the native Promise feature. Promise.all could be an option (in cases of independent parallel async tasks) by passing around an appropriate object, or something decorated with the state, passing it off to whatever is ready when appropriate. In the Promise.all sample below note how it recovers in one of the promises--took me awhile to realize how to redirect the result of a chain. The result of the all is just the last promise's return. While this doesn't answer the question's title, using return Promise.reject(<an-object-including-a-promise>) (or resolve) gives a series and/or group of async tasks shared access and control along the way. In the case of picking, downloading then working with a file I'd take out the progress-event handling then do: pickFile.then(download,orFailGracefully) with downloadProgress handled within the download onResolve handler (download-progress doesn't appear to be an async task). Below are related experiments in the console.
var q = {
defer: function _defer(){
var deferred = { };
deferred.promise = new Promise(function(resolve, reject){
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred;
}
};
var communityThatCares = q.defer();
communityThatCares.promise.then(function(someGood){
console.log('someGood', someGood);
return someGood;
}, function(someBad){
console.warn('someBad', someBad);
return someBad;
});
(new Promise(function(resolve, reject){ communityThatCares.about = 'communityThatCares'; setTimeout(resolve,1000, communityThatCares); }))
.then(
function(e){
console.log(3,e); return e.resolve(e);
}, function(e){
console.warn(3, e); return e.reject(e);
});
var todo = {
find:'swan'
};
var greaterGood = [(
(new Promise(function(res,rej){ res(todo); })).then(function(e){ e.stuff = 'things'; return e; }),
(new Promise(function(res,reject){
reject(todo);
})).then(function(e){ return e; }
,function(e){
console.warn(1,e);
e.recover = 'uh oh';
return Promise.resolve(e);
}).then(function(e){ console.log(2,e); return e; }),
(new Promise(function(res,rej){ res(todo); })).then(function(e){ console.log(1,e); e.schedule = 'today'; return e; },function(e){ console.warn(1,e); return e; }).then(function(e){ console.log(2,e); return e; }))
];
var nkay = Promise.all( greaterGood )
.then(function(todo){
console.log('all',todo[0]); return todo;
}, function(todo){
console.warn('all',todo[0]); return todo;
});

Execute other promise only if first one didn't returned good results

So I'm struggling with this a couple days and I have found a solution for this but I feel like this isn't the good one.
I currently have the following. I don't like it because I'm nesting Promises within promises. I don't know if this is fine but it doesn't look like it.
What I'm trying to accomplish with this code is first to check the cache database for a value if it is not there then I'll check the real database.
Any tips/tricks/pointers/comments on how to do this more elegantly?
var getData = function() {
var cancel = false
var cache = db.getFromCache(query)
.then((data) => {
// Check if data is up to date
if (uptodate) {
return Promise.resolve(data)
}
cancel = true
})
return cache
.then(() => {
if (cancel)
return db.getFromDatabase().then( //code)
}
}
ps: this code may or may not run I just made it quickly for this question. I can't past the real code here
When you're in a .then() handler, you can do anyone of the following:
Return a value - that value becomes the resolved value of the parent promise. So, there is no need to return Promise.resolve(value). You can just return value.
Return a promise - When you return a promise, it is chained to the parent promise and the parent promise will not resolve until this new promise resolves and the resolved value of this returned promise will become the resolved value of the parent promise.
Throw an Exception - If a .then() handler throws, that exception is automatically caught by the promise infrastructure and is turned into a rejection so throw err works similarly to return Promise.reject(err).
As such, when you're in your .then() handler, you can just check to see if the cache data is valid and, if so, just return it. Otherwise, return a new promise to get the data.
var getData = function() {
return db.getFromCache(query).then((data) => {
// Check if data is up to date
if (uptodate) {
// return cached data, will be resolved value of promise
return data;
} else {
// get data from db, return promise that will be chained
return db.getFromDatabase();
}
})
}
getData().then(...)
Your code is way more complicated than need be:
You don't need Promise.resolve(). You can just return the value.
You don't need the cancel variable at all. You can do all your work inside the first .then() handler.
You don't need the second .then() handler.
Promises support chaining, which means that a promise can return another promise, and this one can return another one, and so on.
According to MDN:
You can pass a lambda (anonymous function) to then and if it returns
a promise, an equivalent Promise will be exposed to the subsequent
then in the method chain.
When a value is simply returned from within a then lambda, it will
effectively return Promise.resolve().
This mean that in the then block, you can check if the data is up to date in the cache. If the data is fresh return it, and the value will be wrapped in a new promise. If the data is stale, you can return the call getFromDatabase(), which returns promise:
const getData = (query) => db.getFromCache(query)
.then((data) => isUpToDate(data) ? data : db.getFromDatabase(query));
getData().then(/** code **/);
Returning from a promise wraps the returned data with a new promise, so you can manipulate the data, and return it, and it will be wrapped by a promise automatically:
db.getFromDatabase().then((data) => data.map(/** some code **/)); // result will be wrapped in a promise.

Categories