I have a Meteor.method() that server side returns a promise from oracledb. Client side I have:
Meteor.call('myMethod', (error, result) => {
result.then() // err -> no .then() method?,
});
So what is result? It does not have a .then() method, so it is not a promise?
Meteor does not "send" the promise to the client.
The server returns a result value to the client (which triggers the callback) once the promise is resolved (or rejected) on the server, and not at the moment the promise is returned from the method itself (unless it is already settled when returned).
You can also use async/await to simplify the code.
Here is a blog post with more details about the using asynchronous code in methods.
Note:
The value sent from the server is serialized using EJSON. Object methods, getters, etc. are stripped from it, unless you create a custom serializer. In some cases, serialization might even fail (I think it happened with certain moment objects) and result in undefined being returned.
Meteor is not using promises by default, however, you can wrap your Meteor.calls into a promise function as below
const callWithPromise = (method, myParameters) => {
return new Promise((resolve, reject) => {
Meteor.call(method, myParameters, (err, res) => {
if (err) reject('Something went wrong');
resolve(res);
});
});
}
(async function() {
const myValue1 = await callWithPromise('myMethod1', someParameters);
const myValue2 = await callWithPromise('myMethod2', myValue1);
})();
Sample code has copied from Meteor forum.
Also, this topic gives you a better insight in taking advantages of Aysnc/Await syntax or Promises in Meteor calls.
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 was going through the mongoose docs when I Stumbled upon the line saying
Mongoose queries are not promises. They have a .then() function for co
and async/await as a convenience. If you need a fully-fledged promise,
use the .exec() function.
With this example
var query = Band.findOne({name: "Guns N' Roses"});
assert.ok(!(query instanceof Promise));
// A query is not a fully-fledged promise, but it does have a `.then()`.
query.then(function (doc) {
// use doc
});
// `.exec()` gives you a fully-fledged promise
var promise = query.exec();
assert.ok(promise instanceof Promise);
promise.then(function (doc) {
// use doc
});
Now, I didn't get what they meant when they said fully-fledge promise, like for me .then() should be a promoise and then it also allows async and await.
So can someone please explain me what does fully-fledge promise mean?
Reference link: https://mongoosejs.com/docs/promises.html#queries-are-not-promises
That means that the values returned by queries are thenables per the definition of the Promises/A+ spec, but not actual Promise instances. That means they may not have all of the features of promises (for instance, catch and finally methods). Actual Promise instances would be "fully-fledged" promises.
The English term "fully-fledged" means "complete" or "fully developed." It comes from ornithology (or at least, terminology related to birds): A chick (a young bird) that has its adult feathers is "fledged;" if it has all its adult feathers completely covering its down undercoat, it's fully-fledged.
#T.J. Crowder's answer really helped me clarify a lot of things (I came across this post coz I was confused too), and I just wanted to supplement it a little here :)
Traditionally, the then() method returns a Promise. It takes up to two arguments: callback functions for the success (onFulfilled) and failure (onRejected) cases of the Promise. Thenables, however, does not work this way. We won't be able to pass in two callback functions like how we did with a "fully-fledged promise". To illustrate this with some code:
UserModel.find().exec((err, users) => {
if (err) return res.status(400).send(err);
res.status(200).json({
success: true,
users,
});
});
This works fine because the two callback functions we passed in for the success and failure cases are called err (failure case) and the success/result case is called users.
Note that all callbacks in Mongoose use the pattern: callback(error, result) - this is different from the order of the callback functions specified in the MDN web docs. Now, if we run the same piece of code but replace exec() with then() like so:
UserModel.find().then((err, users) => {
if (err) return res.status(400).send(err);
res.status(200).json({
success: true,
users,
});
});
This returns a 400 Bad Request Status.
This is because Mongoose queries are not "fully-fledged promises" and so we cannot chain a .then() behind a query and then expect it to act like a real promise.
However, from what I've tried, you can actually still use .catch() for error handling like so:
// {_id:1} is included to make this fail
UserModel.find({ _id: 1 })
.then((users) => {
res.status(200).json({
success: true,
users,
});
})
.catch((err) => {
res.status(400).send(err);
});
I'm doing some reading up on JS Promises to up-skill.
Here's my quandry:
Say you want to console.log('we done, bruh!') AFTER your data's come back.
so with a Promise, you might say:
let iWantToLogOut = function() {
let data = fetch('https://jsonplaceholder.typicode.com/users')
return new Promise((resolve) => {
resolve(data)
})
}
And then resolve that promise like:
iWantToLogOut().then((dataBack)
=> databack.json())
.then((json) => {
console.log('We done, bruh! Look: ', json)
})
So that's great. You get your API data back and then we log our msg out.
But isn't it just way easier to go:
let data = fetch('https://jsonplaceholder.typicode.com/users');
data ? console.log('we done, bruh!') : null;
I'm probably over-simplifying/missing something (because... well... i'm retarded) but I just want to make sure i'm really understanding Promises first before i move onto Async/Await.
But isn't it just way easier to go:
let data = fetch('https://jsonplaceholder.typicode.com/users');
data ? console.log('we done, bruh!') : null;
It would be, but it doesn't work. What fetch returns is a promise, not the result of the operation. You can't return the result of an asynchronous process. More: How do I return the response from an asynchronous call?
In the upcoming ES2017 spec, though, we have syntactic sugar around promise consumption which will let you write this:
let data = await fetch('https://jsonplaceholder.typicode.com/users');
// --------^^^^^
console.log('we done, bruh!');
Note we don't even need the conditional, because await converts a promise rejection into an exception.
That code would need to be in an async function, e.g.:
(async function() {
let data = await fetch(/*...*/);
// use data here
})();
The JavaScript engines in some browsers already support async/await, but to use it in the wild, you'll want to transpile with Babel or similar.
Note: You've shown
so with a Promise, you might say:
let iWantToLogOut = function() {
let data = fetch('https://jsonplaceholder.typicode.com/users')
return new Promise((resolve) => {
resolve(data)
})
}
There are a couple of problems with that code:
It never settles the promise you created if the fetch fails.
It calls something data which is not data, it's a promise of data (that's mostly style, but it's misleading).
It exhibits the promise creation anti-pattern. You already have a promise (from fetch), no need to create another.
iWantToLogOut should be simply:
let iWantToLogOut = function() {
return fetch('https://jsonplaceholder.typicode.com/users');
};
That returns a promise that will be resolved with the data, or of course rejected. Which you'd then consume with promise methods or await (within an async function).
It is not a matter of easy.
Usually network calls should be handle asynchronously(I don't want to the anti-pattern of synchronous AJAX calls). At that point you have few options to handle it:
Callbacks
Promises
Observables
In you code above, when it's synchronous, the fetch should return immediately with a promise that will be resolve to the data only when the server has responded. Only then you can check the data for it's content. Further. Because every promise can be fulfilled or failed, in your then you can have a handler for each instead of using the ternary.
From the latest spec:
Synchronous XMLHttpRequest outside of workers is in the process of being removed from the web platform as it has detrimental effects to the end user’s experience. (This is a long process that takes many years.) Developers must not pass false for the async argument when current global object is a Window object. User agents are strongly encouraged to warn about such usage in developer tools and may experiment with throwing an InvalidAccessError exception when it occurs.
I have a file where I'm writing things:
var stream = fs.createWriteStream("my_file.txt");
stream.once('open', function(fd) {
names.forEach(function(name){
doSomething(name);
});
stream.end();
});
This is working ok and I'm able to write to the file.
The problem is that the doSomething() function has some parts that are asynchronous. An example can be given with the dnsLookup function. Somewhere in my doSomething() I have:
dns.lookup(domain, (err, addresses, family) => {
if(err){
stream.write("Error:", err);
}else{
stream.write(addresses);
}
});
Now, my problem is, since the DNS check is asynchronous, the code keeps executing closing the stream. When the DNS response finally comes it cannot write to anywhere.
I already tried to use the async module but it didn't work. Probably I did something wrong.
Any idea?
Now that NodeJS is mostly up to speed with ES2015 features (and I notice you're using at least one arrow function), you can use the native promises in JavaScript (previously you could use a library):
var stream = fs.createWriteStream("my_file.txt");
stream.once('open', function(fd) {
Promise.all(names.map(name => doSomething(name)))
.then(() => {
// success handling
stream.end();
})
.catch(() => {
// error handling
stream.end();
});
});
(The line Promise.all(names.map(name => doSomething(name))) can be simply Promise.all(names.map(doSomething)) if you know doSomething ignores extra arguments and only uses the first.)
Promise.all (spec | MDN) accepts an iterable and returns a promise that is settled when all of the promises in the iterable are settled (non-promise values are treated as resolved promises using the value as the resolution).
Where doSomething becomes:
function doSomething(name) {
return new Promise((resolve, reject) => {
dns.lookup(domain, (err, addresses, family) => {
if(!err){ // <== You meant `if (err)` here, right?
stream.write("Error:", err);
reject(/*...reason...*/);
}else{
stream.write(addresses);
resolve(/*...possibly include addresses*/);
});
});
});
There are various libs that will "promise-ify" Node-style callbacks for you so using promises is less clunky than the mix above; in that case, you could use the promise from a promise-ified dns.lookup directly rather than creating your own extra one.
I am trying to utilize request with Bluebird's Promises:
const request = Promise.promisify(require('request'));
Promise.promisifyAll(request);
Unfortunately, the result I am getting does not reflect what I was expected based on examples:
request('http://google.com').then(function(content) {
// content !== String
// Object.keys(content) => ['0', '1']
};
content is not a string
I have to access the content via content['0'] respectively content['1'] and here my actually expected response, the HTML string is.
This seems fishy to me, like I misused the Promise API here. What have I done wrong?
Use .spread(function(response, content){}) instead of .then(function(content){}).
Why not to use native node's Promises?
function req() {
return new Promise (function(resolve, reject){
request('http://google.com',function (error, response, body){
if (error)
reject(error);
resolve(body) //if json, JSON.parse(body) instead of body
});
});
}
req().then(data => doSomethingWithData);
You should use spread() instead of then().
Here is a link of bluebird, which explains the usage of spread().
https://github.com/petkaantonov/bluebird/blob/2.x/API.md#spreadfunction-fulfilledhandler--function-rejectedhandler----promise
All the callback function of request is comprised of three params (err, response, content). In promisifed request, err is handled by catch().
But how to pass the other two parameters to the next chained callback function? It is not able to be achieved by then().
So promisifed request will return an array of two promises, one will return repsonse and the other content. And they can be caught by the spread() method, which is able to catch a fixed-size array of promises.