Why can't I use Promise.resolve with an osmosis instance? - node.js

I am trying to understand why these console.log statements behave differently. I expect them to behave the same:
Using Node 7. Consider the following cases:
1. Promise.resolve(object)
Promise.resolve handles objects as I'd expect:
Promise.resolve({ a: `hello` }).then(console.log) // { a:'hello' }
2. Directly console.log a class instance from a library.
If I store an Osmosis instance I can console.log it:
const osmosis = require(`osmosis`)
console.log(new osmosis.get(url))
/* { prev:
{ instance: Osmosis:1,
name: 'get',
args: [ 'http://www.google.com', , ],
getURL: [Function: getURLArg],
url: 'http://www.google.com',
params: undefined,
cb: [Function: Get],
next: [Circular] } }
*/
3. Promise.resolve(class instance)
But if I try to resolve an Osmosis instance I don't see parity:
Promise.resolve(new osmosis.get(url)).then(console.log) // nothing
What's going on here? Am I misunderstanding something about Promise.resolve()...? Or console.log?
Why doesn't [3] log the same as [2], given the behavior in [1]?
Context: I don't think my immediate practical goals matter to answering this question. But here you go just in case. I don't see how anything about the library itself should affect the output of the final example. Here's the docs on that new osmosis.get() though: http://rchipka.github.io/node-osmosis/Osmosis.html#toc1__anchor
new osmosis.get(url) doesn't perform an async http request. It instantiates an instance of the scraper, which can be built up with a set of declarative instructions and told to "run" at some arbitrary later time.
I want to be able to build this set of instructions in a promise chain for several reasons.
The main one is that it would be the easiest way to break up the instruction definitions into different functions that are easier to test and understand. e.g. instead of osmosis.get(url).set({some stuff}).find(#something), I'd like to:
function defineSearch(instance){
return instance.set({some stuff})
}
function definePath(instance) {
return instance.find(#something)
}
Promise.resolve(new osmosis.get(url))
.then(defineSearch)
.then(definePath)
.then(instance => instance.run())

The documentation is horrible and uses techniques that are rather unconventional. What new osmosis.get(url) returns is not an Osmosis instance but a Command one. And those do have a then method.
When you pass something to Promise.resolve, it is tested for being a thenable or not, and if it looks like a promise it is tried to be assimilated: a callback is passed into the then method which will resolve the new promise.
So when you do Promise.resolve(new osmosis.get(url)), you get back an unresolved promise that will fulfill when the then callback is called (which happens when you run the command). In your case, it never does.
The solution to your particular problem is not to use promises at all (since you're not doing anything asynchronous):
definePath(defineSearch(new osmosis.get(url))).run())
but you probably should also report a bug that Commands look like promises with being properly thenable, which breaks a lot of things in ES6.

Related

Mocha/Chai test keeps producing error "cannot read property 'length' of undefined," but the function works in the app itself

Problem summary:
I wrote a function called getImagePathsFromCatalog that takes a string representing the location of a catalog (XML) file. It reads product info from the catalog file and produces an array "map"(*) consisting of productIds (key), each with an array of their associated images (value).
In my application, this runs fine and always returns the expected output, which should look something like this (formatted for readability):
[
'TEST-123': [ 'products/TEST-123.jpg' ],
'TEST-12345': [ 'products/Equipment/TEST-12345.jpg',
'products/Equipment/TEST-12345_1.jpg' ]
]
Background Info:
I have tried using various syntax to construct the it block (using async/await, using .then); using return and expect; using various Chai methods (be, should, assert, etc.); assigning the resolve value of the promise to a let for use in the assertion; using done; and some other things I'm sure I'm forgetting.
I have also tried different expectations, including to.be.array, to.be.ok, to.exist, and etc., but all end in the same error.
I have installed and am importing all of the required libraries and functions: chai, chai-as-promised, chai-arrays, chai-asserttype, chai.expect (etc.) and am also "using" them, as in chai.use(chaiAsPromised), etc.
(*) Note: Due to project specs, I have to use an array that behaves more or less like a map, rather than using an actual map object. I know this may cause issues with tests like to.be.array, but it shouldn't prevent me from expecting things like the result existing or coming back non-null (at least I wouldn't think it would?.)
Code:
describe('catalogImages.getImagePathsFromCatalog(catalogFile)', function () {
it('function should return an array', function() {
catalogImages.getImagePathsFromCatalog('../imports/catalog.xml')
.then(catalogImagePathsMap => {
return expect(catalogImagePathsMap).to.be.array();
});
});
});
Expected and actual result summary:
Expected result: Some tests (even just basic assertions about the existence or non-null/non-undefined status of the result) should pass.
Actual result: All tests written for this function return the same error ("cannot read property 'length' of undefined").
I've been researching this error and haven't found a solution that works for me yet. Thank you in advance for any assistance.
Looking at the chai expect documentation, it looks like array expectations are managed through the following syntax:
expect(foo).to.be.an('array');
Can you try this? You may need to update to the latest chai version.
Very unexpected, but the issue was actually the file path I was passing-into the function as an argument. The path was referencing the file from the test's location in the file system, but when my coworker suggested doing a console.log(process.cwd()); in the test just to double-check (after trying A LOT of other things that seemed more obvious), we found that the file was behaving as if it was in the root instead of in its subfolder. After switching the path argument to start at the root level rather than the subfolder the test file resides in, the tests started working perfectly. I still don't understand why this is the case, but maybe this will help someone else in the future who is banging their head against a similar Mocha/Chai mystery!
Doesn't work:
describe('catalogImages.getImagePathsFromCatalog(catalogFile)', function () {
it('function should return an array', function() {
catalogImages.getImagePathsFromCatalog('../imports/catalog.xml')
.then(catalogImagePathsMap => {
return expect(catalogImagePathsMap).to.be.array();
});
});
});
Does work:
describe('catalogImages.getImagePathsFromCatalog(catalogFile)', function () {
it('function should return an array', function() {
catalogImages.getImagePathsFromCatalog('./imports/catalog.xml')
.then(catalogImagePathsMap => {
return expect(catalogImagePathsMap).to.be.array();
});
});
});

Wrapping/intercepting a promise in an inner function

I'm having difficulty finding an answer to my question, perhaps because I don't know how to ask it (what search terms to use). I'm really struggling to understand promises, and have watched a number of tutorial videos and am still not getting some fundamental piece to make it click.
In Node, I am using the request-promise module, which returns a promise when I make a call to an rp() method. I am calling
return rp(service);
from within a function. But what I want to do, instead, is add a .then() handler to this to do some post-processing with the result of the service call, BEFORE returning the promise back to the caller so the caller can still have its own then handler.
How do I accomplish this?
It would be helpful to see your current code to better understand where exactly you are struggling with.
In general, you can think of promises as a placeholder for some future value that you can chain together.
If I understood your question correctly, you would like to make a request to a server, receive the response, do something with the response and return it back to the caller.
const callService = (url) => {
// make sure to return your promise here
return rp(url)
.then((html) => {
// do something with html
return html;
});
};
Now you can call it like so -
callService('http://www.google.com')
.then((html) => {
console.log(html);
}).catch((err) => {
// error handling
});
The common mistake is to omit the return statement in your top level function and subsequent then functions.
Feel free to provide additional details.

Node.js: serial operations with branching

I'm new to node.js and using it for a backend that takes data from syslog messages and stores it to a database.
I've run into the following type of serial operations:
1. Query the database
2. If the query value is X do A. otherwise do B.
A. 1. Store "this" in DB
2. Query the database again
3. If the query value is Y do P. otherwise do Q.
P. Store "something"
Q. Store "something else"
B. 1. Store "that" in the DB
2. Store "the other thing" in the DB
The gist here is that I have some operations that need to happen in order, but there is branching logic in the order.
I end up in callback hell (I didn't know what that is when I came to Node.. I do now).
I have used the async library for things that are more straight forward - like doing things in order with async.forEachOfSeries or async.queue. But I don't think there's a way to use that if things have to happen in order but there is branching.
Is there a way to handle this that doesn't lead to callback hell?
Any sort of complicated logic like this is really, really going to benefit from using promises for every async operation, especially when you get to handling errors, but also just for structuring the logic flow.
Since you haven't provided any actual code, I will make up an example. Suppose you have two core async operations that both return a promise: query(...) and store(...).
Then, you could implement your above logic like this:
query(...).then(function(value) {
if (value === X) {
return store(value).then(function() {
return query(...).then(function(newValue) {
if (newValue === Y) {
return store("something");
} else {
return store("something else");
}
})
});
} else {
return store("that").then(function() {
return store("the other thing");
});
}
}).then(function() {
// everything succeeded here
}, function(err) {
// error occurred in anyone of the async operations
});
I won't pretend this is simple. Implemented logic flow among seven different async operation is just going to be a bit of code no matter how you do it. But, promises can make it less painful than otherwise and massively easier to make robust error handling work and to interface this with other async operations.
The main keys of promises used here are:
Make all your async operations returning promises that resolve with the value or reject with the failure as the reason.
Then, you can attach a .then() handler to any promise to see when the async operation is finished successfully or with error.
The first callback to .then() is the success handler, the second callback is the error handler.
If you return a promise from within a .then() handler, then that promise gets "chained" to the previous one so all success and error state becomes linked and gets returned back to the original promise state. That's why you see the above code always returning the nested promises. This automates the returning of errors all the way back to the caller and allows you to return a value all the way back to the caller, even from deeply nested promises.
If any promise in the above code rejects, then it will stop that chain of promises up until an actual reject handler is found and propagate that error back to that reject handler. Since the only reject handler in the above code is at the to level, then all errors can be caught and seen there, no matter how deep into the nested async mess it occurred (try doing that reliably with plain callbacks - it's quite difficult).
Native Promises
Node.js has promises built in now so you can use native promises for this. But, the promise specification that node.js implements is not particularly feature rich so many folks use a promise library (I use Bluebird for all my node.js development) to get additional features. One of the more prominent features is the ability to "promisify" an existing non-promise API. This allows you to take a function that works only with a callback and create a new function that works via a promise. I find this particularly useful since many APIs that have been around awhile do not natively return promises.
Promisifying a Non-Promise Interface
Suppose you had an async operation that used a traditional callback and you wanted to "promisify it". Here's an example of how you could do that manually. Suppose you had db.query(whatToSearchFor, callback). You can manually promisify it like this:
function queryAsync(whatToSearchFor) {
return new Promise(function(resolve, reject) {
db.query(whatToSearchFor, function(err, data) {
if (!err) {
reject(err);
} else {
resolve(data);
}
});
});
}
Then, you can just call queryAsync(whatToSearchFor) and use the returned promise.
queryAsync("foo").then(function(data) {
// data here
}, function(err) {
// error here
});
Or, if you use something like the Bluebird promise library, it has a single function for promisifying any async function that communicates its result via a node.js-style callback passed as the last argument:
var queryAsync = Promise.promisify(db.query, db);

Using this within a promise in AngularJS

Is there a best-practice solution to be able to use within in promise this? In jQuery i can bind my object to use it in my promise/callback - but in angularJS? Are there best-practice solutions? The way "var service = this;" i don't prefer ...
app.service('exampleService', ['Restangular', function(Restangular) {
this._myVariable = null;
this.myFunction = function() {
Restangular.one('me').get().then(function(response) {
this._myVariable = true; // undefined
});
}
}];
Are there solutions for this issue? How i can gain access to members or methods from my service within the promise?
Thank you in advance.
The generic issue of dynamic this in a callback is explained in this answer which is very good - I'm not going to repeat what Felix said. I'm going to discuss promise specific solutions instead:
Promises are specified under the Promises/A+ specification which allows promise libraries to consume eachother's promises seamlessly. Angular $q promises honor that specification and therefor and Angular promise must by definition execute the .then callbacks as functions - that is without setting this. In strict mode doing promise.then(fn) will always evaluate this to undefined inside fn (and to window in non-strict mode).
The reasoning is that ES6 is across the corner and solves these problems more elegantly.
So, what are your options?
Some promise libraries provide a .bind method (Bluebird for example), you can use these promises inside Angular and swap out $q.
ES6, CoffeeScript, TypeScript and AtScript all include a => operator which binds this.
You can use the ES5 solution using .bind
You can use one of the hacks in the aforementioned answer by Felix.
Here are these examples:
Adding bind - aka Promise#bind
Assuming you've followed the above question and answer you should be able to do:
Restangular.one('me').get().bind(this).then(function(response) {
this._myVariable = true; // this is correct
});
Using an arrow function
Restangular.one('me').get().then(response => {
this._myVariable = true; // this is correct
});
Using .bind
Restangular.one('me').get().then(function(response) {
this._myVariable = true; // this is correct
}.bind(this));
Using a pre ES5 'hack'
var that = this;
Restangular.one('me').get().then(function(response) {
that._myVariable = true; // this is correct
});
Of course, there is a bigger issue
Your current design does not contain any way to _know when _myVariable is available. You'd have to poll it or rely on internal state ordering. I believe you can do better and have a design where you always execute code when the variable is available:
app.service('exampleService', ['Restangular', function(Restangular) {
this._myVariable =Restangular.one('me');
}];
Then you can use _myVariable via this._myVariable.then(function(value){. This might seem tedious but if you use $q.all you can easily do this with several values and this is completely safe in terms of synchronization of state.
If you want to lazy load it and not call it the first time (that is, only when myFunction is called) - I totally get that. You can use a getter and do:
app.service('exampleService', ['Restangular', function(Restangular) {
this.__hidden = null;
Object.defineProperty(this,"_myVariable", {
get: function(){
return this.__hidden || (this.__hidden = Restangular.one('me'));
}
});
}];
Now, it will be lazy loaded only when you access it for the first time.

Block function whilst waiting for response

I've got a NodeJS app i'm building (using Sails, but i guess that's irrelevant).
In my action, i have a number of requests to other services, datasources etc that i need to load up. However, because of the huge dependency on callbacks, my code is still executing long after the action has returned the HTML.
I must be missing something silly (or not quite getting the whole async thing) but how on earth do i stop my action from finishing until i have all my data ready to render the view?!
Cheers
I'd recommend getting very intimate with the async library
The docs are pretty good with that link above, but it basically boils down to a bunch of very handy calls like:
async.parallel([
function(){ ... },
function(){ ... }
], callback);
async.series([
function(){ ... },
function(){ ... }
]);
Node is inherently async, you need to learn to love it.
It's hard to tell exactly what the problem is but here is a guess. Assuming you have only one external call your code should look like this:
exports.myController = function(req, res) {
longExternalCallOne(someparams, function(result) {
// you must render your view inside the callback
res.render('someview', {data: result});
});
// do not render here as you don't have the result yet.
}
If you have more than two external calls your code will looks like this:
exports.myController = function(req, res) {
longExternalCallOne(someparams, function(result1) {
longExternalCallTwo(someparams, function(result2) {
// you must render your view inside the most inner callback
data = {some combination of result1 and result2};
res.render('someview', {data: data });
});
// do not render here since you don't have result2 yet
});
// do not render here either as you don't have neither result1 nor result2 yet.
}
As you can see, once you have more than one long running async call things start to get tricky. The code above is just for illustration purposes. If your second callback depends on the first one then you need something like it, but if longExternalCallOne and longExternalTwo are independent of each other you should be using a library like async to help parallelize the requests https://github.com/caolan/async
You cannot stop your code. All you can do is check in all callbacks if everything is completed. If yes, go on with your code. If no, wait for the next callback and check again.
You should not stop your code, but rather render your view in your other resources callback, so you wait for your resource to be reached before rendering. That's the common pattern in node.js.
If you have to wait for several callbacks to be called, you can check manually each time one is called if the others have been called too (with simple bool for example), and call your render function if yes. Or you can use async or other cool libraries which will make the task easier. Promises (with the bluebird library) could be an option too.
I am guessing here, since there is no code example, but you might be running into something like this:
// let's say you have a function, you pass it an argument and callback
function myFunction(arg, callback) {
// now you do something asynchronous with the argument
doSomethingAsyncWithArg(arg, function() {
// now you've got your arg formatted or whatever, render result
res.render('someView', {arg: arg});
// now do the callback
callback();
// but you also have stuff here!
doSomethingElse();
});
});
So, after you render, your code keeps running. How to prevent it? return from there.
return callback();
Now your inner function will stop processing after it calls callback.

Resources