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
Related
I want this function tagFindOrCreate to only execute if the first function is finished:
func.tweetCreate(bmTweet, user)
.then(function () {
func.tagFindOrCreate(bmTweet, bmtTag, user)
})
Help me create a promise for this function:
tweetCreate: function (tweet, user) {
Tweet.create(tweet, function (err, tweet) {
user[0].tweets.push(tweet)
user[0].save()
console.log("Tweet saved.")
return Promise.resolve()
})
}
This would be as follows:
tweetCreate: function (tweet, user) {
return new Promise(resolve => {
Tweet.create(tweet, function (err, tweet) {
user[0].tweets.push(tweet);
user[0].save();
console.log("Tweet saved.")
resolve();
})
}
};
Since Tweet is a mongoose model, its create method will already return a promise as long as you don't pass a callback. This means you can move your callback logic to where promise resolution occurs. The simplest way to do this would be to use an async function.
tweetCreate: async function (tweetContents, user) {
const createdTweet = await Tweet.create(tweetContents);
user[0].tweets.push(createdTweet);
user[0].save();
console.log('tweet saved');
}
A note on error handling: An async function will already return a promise or potentially throw an error (if there is an error in Tweet.create). The above code will propagate the error, so make sure that if you are using async/await you wrap your calling code in try/catch or make use of .catch to handle the errors where it's needed. It's up to the logic of your app to decide whether you need to catch the error in tweetCreate itself, or whether you can propagate it into the application code level where it can be caught and handled, but just be aware of the need to handle potential promise errors.
For the sake of completeness, you can promisify Tweet.create. It's common for library methods to return promises nowadays, so manual promisification is rarely necessary. Keep this in mind if you feel the need to promisify ... the library may already have a mechanism for it.
You can do this manually simply by using a Promise constructor. This takes a callback which takes a resolve argument which you can call from within another callback to resolve the promise:
return Promise(resolve => {
Tweet.create(tweet, (error, createdTweet) {
...
resolve();
});
});
Node.js has a built-in promisify function as well, so using a Promise constructor is probably unnecessary for this purpose. You could do:
const createTweet = util.promisify(Tweet.create);
const createdTweet = await createTweet(tweet);
...
NOTE: the above is just an example. It's not necessary to do this with mongoose models.
You need to use the promise constructor to create a promise and tweetCreate() should return that promise.
Call to Tweet.create(...) should be inside the executor function (function that is passed to the promise constructor) and from inside of the callback function of Tweet.create(...), you need to call the resolve() function to fulfil the promise.
You need to change the implementation of your function to as shown below:
tweetCreate: function (tweet, user) {
return new Promise((resolve, reject) => {
Tweet.create(tweet, function (err, tweet) {
user[0].tweets.push(tweet);
user[0].save();
resolve(); // <--- this will resolve the promise
});
});
}
Calling resolve() will fulfil the promise leading to the invocation of the callback function of the then() method.
Edit
You mentioned in a comment that Tweet is a mongoose model; In that case, you don't need a explicitly create a promise. Mongoose provides a promise-based API for creating and saving documents in the database and that is what you should use instead of creating promises yourself.
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()
I have abstraction:
function fetchDataFromAPI() {
const url = `https://api...`
return fetch(url).then(response => response.json())
}
I want to use it in my other piece of code like:
if(something){
const data = fetchDataFromAPI()
return data
}
if I console.log data what I get is resolved pending promise
PromiseĀ {<pending>}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: Object
How do I get that Object in data instead of Promise?
You can not. Here is why:
Promise is a language construct that makes JavaScript engine to continue to execute the code without waiting the return of inner function, also known as the executor function. A promise always run inside the event loop.
var p = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 300);
});
console.log(p);
Basically a promise is a glorified syntactic sugar for a callback. We will see how but first lets have a more realistic code:
function someApiCall(){
return new Promise(function(resolve, reject){
setTimeout(()=>{
resolve('Hello');
})
})
}
let data = someApiCall();
console.log(data);
This is a so-called asynchronous code, when JavaScript engine executes it, someApiCall immediately returns a result, in this case pending promise:
> Promise {<pending>}
If you pay attention to the executor, you will see we needed to pass resolve and reject arguments aka callbacks. Yes, they are callbacks required by the language construct. When either of them called, promise will change its state and hence be settled. We don't call it resolved because resolving implies successful execution but a function also can error out.
How do we get the data? Well we need more callbacks, which will be called by the executor function once the promise is settled:
var p = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 300);
});
p.then((result) => {
console.log(result); // foo
}).catch((err) => {
console.log(err);
});
Why we need to pass separate callbacks? Because one will be fed to the resolve, and the other to the reject. Then callback will be called by the resolve function, the catch callback by the reject function.
Javascript engine will execute these callbacks later on its leisure, for a regular function it means when the event loop is cleared, for timeout when the time is up.
Now to answer your question, how do we get data out from a promise. Well we can't.
If you look closely, you will see we don't really get the data out but keep feeding callbacks. There is no getting data out, but passing callbacks in.
p.then((result) => {
console.log(result);
}).catch((err) => {
console.log(err);
});
Some say use await:
async function() {
let result = await p;
}
But there is a catch. We have to or wrap it in async function. Always. Why? Because Async/await is another level of abstraction or syntactic sugar, whichever you prefer, on top of promise api. That is why we can not use await directly but always wrap it in async statement.
To sum up, when we use promise or async/await we need to follow certain convention and write terse code with closely knitted callbacks. Either javascript engine or transpilers like babeljs or typescript converts these code to regular javascript to be run.
I can understand your confusion because people keep saying getting data out when talking about promises, but we don't get any data out but pass callback to be executed when the data is ready.
Hope everything is clear now.
No, you cannot without using promises or async/await etc because calling a REST API is an asynchronous operation and is non blocking.
When you make a call to a REST API, the code shouldn't wait until the API returns a value because it may take a lot of time, making program non-responsive, thus by design making a network request is categorized as an asynchronous operation.
To avoid async/await, you'll need to use another .then:
if (something) {
return fetchDataFromAPI()
.then((data) => data /* you can console.log(data) here */)
}
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.
I am using AngularJS and Typescript. In my controller constructor, I make a call to a function that gets data and returns a promise. The promise is stored in the variable x.
x.then((data) => {
//displays data
}, (err) => {
//sends error message
});
Right now if the promise never resolves, it displays nothing. I want to use $timeout so that if the resolution of x takes too long it will display another message. I don't know how I can do it for promises. Calling this.$timeout(3000,true).then(() => {}); just delays the contents and I can delay a function. How can I abort if the promise doesn't resolve?
AngularJS v1.6 introduces the $q.race method which can be used to abort a promise:
var timeoutPromise = $timeout(null, delay).then(() => {throw "Timeout Abort" });
var timedPromise = $q.race(xPromise, timeoutPromise);
In this example, timedPromise will either resolve (fulfilled or rejected) with the results of xPromise or be rejected with the reason "Timeout Abort" whichever comes first.