Intern: Chaining operations not returning promise - javascript

Using Intern I have to get an hidden json object from the page and then build a dictionary. After this, querying this dictionary I should perform an other action on the DOM.
The problem is that I do not know how to bind these 2 things, because I want the second operation is executed after the first one is completed.
My code is something like:
var self._formMap = null;
if(self._formMap === null || Object.keys(self._formMap).length === 0) {
return remote.findByXpath(selector)
.getAttribute('value')
.then(function(value) {
var jsonValue = JSON.parse(value);
var formMap = {};
for (var item in jsonValue) {
if (jsonValue.hasOwnProperty(item)) {
var key = jsonValue[item][0].split(/[\/]+/).pop();
formMap[key] = item;
}
}
return formMap;
}).then(function (map) {
self._formMap = map;
return _super_.setInputInForm.call(this, [..., formMap, ..]); // function in another file, but that shares the same remote object.
});
}
In the second step, when I call the setInputInForm, it's like the remote is undefined. Is it maybe because I'm returning the formMap in the first step? Could be a problem of promise?
Furthermore, I would like to isolate the first steps, and put it into a function, always returning a promise.
Thanks.

Related

Javascript property function execution

I have an model Object. One of the properties is a function to connect to an api and retrieve some values. I may have this out of order or need to create some sort of "Helper" function to do this correctly but I wanted to ask this question first.
var BuildInfo = {
arch: function getArch(){
// connect to api and return values
}
}
module.exports = BuildInfo;
How can I get this populate the arch property or do I need getArch function somewhere else and just return the results as an array to the arch property?
One strange idea comes to mind, if you really want something that looks like a property but is actually an asynchronous function call. Within an async function, you can var array = await BuildInfo.arch, assuming it looks like this:
var BuildInfo = {
get arch() {
// connect to api and return values
return someAsynchronousAPIThatReturnsAPromise()
}
}
module.exports = BuildInfo;
Demo
var BuildInfo = {
get arch() {
return Promise.resolve(['arch', 'info', 'array'])
}
}
async function getMyArchInfo() {
var array = await BuildInfo.arch
console.log(array)
}
getMyArchInfo()

Compare functions in Javascript

I have an API that takes a function as an input, and then inside the API, the intent is to add the function to an Array if the function is not already added to the Array.
The call to the API is of the form:
myApiHandle.addIfUnique(function(){
myResource.get(myObj);
});
The API is:
myApiHandle.addIfUnique(myFunc) {
if (myArray.indexOf(myFunc) === -1) {
return;
}
// add to array
}
Now this obviously does not work as expected, since each time a new function is being passed in.
My Question is: Is there a way to pass in a function into the myApiHandle.addIfUnique call that will allow me to compare the existing functions in the array with this function that is currently passed in? The comparison should compare the function name and the object, and if both are the same, then not add the function to the array. I want to avoid adding another argument to the addIfUnique call if at all possible.
In other words, is the below possible:
myApiCall.addIfUnique (someFunc) {
}
If so, what is the someFunc. And what would be the logic inside the API to detect if the function already exists in myArray?
The same problem occurs with addEventListener and removeEventListener, where the callback must be identical (in the === sense) for removeEventListener to remove it.
As you've found, obviously if you call addIfUnique like this:
addIfUnique(function() { })
the function passed each time will be a unique object. The solution is to create the function once:
var fn = function() { };
addIfUnique(fn);
addIfUnique(fn);
A related problem occurs when the function being passed in is a method invocation, so I need to bind it:
var x = { val: 42, method: function() { console.log(this.val); } };
I want to pass a bound version of it, so
addIfUnique(x.method.bind(x));
addIfUnique(x.method.bind(x));
But again, each call to x.method.bind(x) will return a separate function. So I need to pre-bind:
var boundMethod = x.method.bind(x);
addIfUnique(boundMethod);
addIfUnique(boundMethod);
First of all, comparing functions is meaningless, even if two functions are literally different, they may be functionally the same.
And for your problem, you can compare whether it's exactly the same object, or you can compare it literally by using toString() function and regExp.
var addIfUnique = (function() {
var arr = [];
return function(func) {
if (~arr.indexOf(func)) return false;
var nameArr = [];
var funcName = func.name;
var funcRegExp = new RegExp('[^\{]+\{(.+)\}$', 'i');
var funcStr = func.toString().match(funcRegExp);
funcStr = funcStr && funcStr[1];
if (!funcStr) return false;
var strArr = arr.map(function(v){
nameArr.push(v.name);
return v.toString().match(funcRegExp)[1];
});
if (~strArr.indexOf(funcStr) && ~nameArr.indexOf(funcName)) return false;
arr.push(func);
};
}());

Add methods to a collection returned from an angular resource query

I have a resource that returns an array from a query, like so:
.factory('Books', function($resource){
var Books = $resource('/authors/:authorId/books');
return Books;
})
Is it possible to add prototype methods to the array returned from this query? (Note, not to array.prototype).
For example, I'd like to add methods such as hasBookWithTitle(title) to the collection.
The suggestion from ricick is a good one, but if you want to actually have a method on the array that returns, you will have a harder time doing that. Basically what you need to do is create a bit of a wrapper around $resource and its instances. The problem you run into is this line of code from angular-resource.js:
var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
This is where the return value from $resource is set up. What happens is "value" is populated and returned while the ajax request is being executed. When the ajax request is completed, the value is returned into "value" above, but by reference (using the angular.copy() method). Each element of the array (for a method like query()) will be an instance of the resource you are operating on.
So a way you could extend this functionality would be something like this (non-tested code, so will probably not work without some adjustments):
var myModule = angular.module('myModule', ['ngResource']);
myModule.factory('Book', function($resource) {
var service = $resource('/authors/:authorId/books'),
origQuery = service.prototype.$query;
service.prototype.$query = function (a1, a2, a3) {
var returnData = origQuery.call(this, a1, a2, a3);
returnData.myCustomMethod = function () {
// Create your custom method here...
return returnData;
}
}
return service;
});
Again, you will have to mess with it a bit, but that's the basic idea.
This is probably a good case for creating a custom service extending resource, and adding utility methods to it, rather than adding methods to the returned values from the default resource service.
var myModule = angular.module('myModule', []);
myModule.factory('Book', function() {
var service = $resource('/authors/:authorId/books');
service.hasBookWithTitle = function(books, title){
//blah blah return true false etc.
}
return service;
});
then
books = Book.list(function(){
//check in the on complete method
var hasBook = Book.hasBookWithTitle(books, 'someTitle');
})
Looking at the code in angular-resource.js (at least for the 1.0.x series) it doesn't appear that you can add in a callback for any sort of default behavior (and this seems like the correct design to me).
If you're just using the value in a single controller, you can pass in a callback whenever you invoke query on the resource:
var books = Book.query(function(data) {
data.hasBookWithTitle = function (title) { ... };
]);
Alternatively, you can create a service which decorates the Books resource, forwards all of the calls to get/query/save/etc., and decorates the array with your method. Example plunk here: http://plnkr.co/edit/NJkPcsuraxesyhxlJ8lg
app.factory("Books",
function ($resource) {
var self = this;
var resource = $resource("sample.json");
return {
get: function(id) { return resource.get(id); },
// implement whatever else you need, save, delete etc.
query: function() {
return resource.query(
function(data) { // success callback
data.hasBookWithTitle = function(title) {
for (var i = 0; i < data.length; i++) {
if (title === data[i].title) {
return true;
}
}
return false;
};
},
function(data, response) { /* optional error callback */}
);
}
};
}
);
Thirdly, and I think this is better but it depends on your requirements, you can just take the functional approach and put the hasBookWithTitle function on your controller, or if the logic needs to be shared, in a utilities service.

Javascript Scope Issues for inner-function

Pretty sure this has been asked already, but I don't know what to search for. Anyway,
var livemarks = [];
var livemarkIds = PlacesUtils.annotations.getItemsWithAnnotation("livemark/feedURI", {});
for (var i = 0; i < livemarkIds.length; i++){
PlacesUtils.livemarks.getLivemark( {id : livemarkIds[i]}, function(result, livemark){
if (result == Components.results.NS_OK){
livemarks.push(livemark);
}
});
}
alert(livemarks.length);
I am trying to play a bit with a Firefox addon that's no longer maintained by its creator, just to learn a bit. I recently got an error saying getFeedURI is going to be deprecated and I want to change his old function.
EDIT:
From a function defined in a function (inner function), I am unable to access a var defined in the parent. Why?
E.g. I cannot access var livemarks from inside getLivemark(), or other similar internal functions.
I was checking (scroll down completely): this and his code works fine. So what's wrong with my code? I just wanted to avoid the recursion, if possible.
I suspect the PlacesUtils.livemarks.getLivemark function does its work asynchronously, so your callback is called after you alert the length. Put your alert inside the callback and you should see the correct length (eventually). Here's one way:
var expecting = livemarkIds.length;
for (var i = 0; i < livemarkIds.length; i++){
PlacesUtils.livemarks.getLivemark( {id : livemarkIds[i]}, function(result, livemark){
if (result == Components.results.NS_OK){
livemarks.push(livemark);
// ***New stuff***
if (livemarks.length === expecting) {
// Now you have them all, now you can do the next thing
doSomethingWithTheLiveMarks(livemarks);
}
}
});
}
Note that there I put livemarkIds.length into expecting, just in case you do other things with livemarkIds while the function is running. If you aren't, you can just use that directly.
Re your comment below:
However, the system works like this: I get the livemarks in an array. This code is in a class (and method) actually, so another class initializes this one and will call the function getFeeds(), which will return that array of livemarks.
If PlacesUtils.livemarks.getLivemark is asynchronous, it's impossible for getFeeds to return the array as a return value. E.g., it cannot be used like this:
a = b;
c = 42;
feeds = getFeeds(feedIds);
if (feeds.length === 0) {
// Do something
}
else {
// Do something else
}
The good news is it's really easy to fix: Have getFeeds accept a callback function that it calls when it has the feeds. The code above changes to look like this:
a = b;
c = 42;
feeds = getFeeds(feedIds, function(feeds) {
if (feeds.length === 0) {
// Do something
}
else {
// Do something else
}
});
As you can see, it's a pretty straightforward change. Assuming the loop above is all of getFeeds, then getFeeds ends up looking something like this:
function getFeeds(livemarkIds, callback) {
var livemarks = [];
for (var i = 0; i < livemarkIds.length; i++){
PlacesUtils.livemarks.getLivemark( {id : livemarkIds[i]}, function(result, livemark){
if (result == Components.results.NS_OK){
livemarks.push(livemark);
if (livemarks.length === livemarkIds.length) {
// Done, trigger the callback
callback(livemarks);
}
}
});
}
}
And the pattern continues: If the code calling getFeeds is being called by something else that's expecting a return value from the async stuff, instead of returning that value, you have that code accept a callback, and call the callback from the getFeeds callback. And so on.
Once you get used to it, it's very easy to do. Getting used to it can be tricky. :-)

Variable scope in Javascript Object

I'm discovering the concept of "objects" in JavaScript. I'm making an RSS Parser, and I have an error (commented).
function MyParser (feed_url) { // Construct
"use strict";
this.feedUrl = feed_url;
this.pubArray = [];
if (typeof (this.init_ok) == 'undefined') {
MyParser.prototype.parse = function () {
"use strict";
var thisObj = this;
$.get(this.feedUrl, function (data, textStatus, jqXHR) {
if (textStatus == 'success') {
var xml = jqXHR.responseXML,
//lastBuildDate = new Date($(xml).find('lastBuildDate').text());
items = $(xml).find('item');
items.each(function () {
var pubSingle = thisObj.makeObj($(this).find('pubDate').text(),
$(this).find('link').text(),
$(this).find('title').text(),
$(this).find('description').text(),
$(this).find('encoded').text(),
$(this).find('commentRss').text(),
$(this).find('comments').last().text());
thisObj.pubArray.push(pubSingle);
});
console.log(thisObj.pubArray); // OK
}
}, 'xml');
console.log(this.pubArray); // Empty
return (this.pubArray);
};
MyParser.prototype.makeObj = function (pubDate, pubLink, pubTitle, pubDesc, pubContent, pubComCount, pubComLink) {
"use strict";
var pubSingle = {};
pubSingle.pubDate = new Date(pubDate);
pubSingle.pubLink = pubLink;
pubSingle.pubTitle = pubTitle;
pubSingle.pubDesc = pubDesc;
pubSingle.pubContent = pubContent;
pubSingle.pubComCount = pubComCount;
pubSingle.pubComLink = pubComLink;
return (pubSingle);
};
}
this.init_ok = true;
}
If you look at the console.log(), you'll see that the line // OK is outputting my array correctly.
But later, when returning from $.get, my array is empty.
Does anybody have an idea why, and how to correct that please?
This is not a problem with variable-scope. The problem here is that you're working with asynchronous flow and you're not thinking correctly the flow.
Let me explain:
When you do your .get, you fire a parallel asynchronous process that will request information from the browser, but your main program's flow keeps going, so when you get to your "return" statement, your array has not been filled yet with the response from your get method.
You should use your array from inside the get callback and not outside of it, since you can't guarantee that the array will have the information you need.
Does it make any sense?
Let me know!
Further explanation
According to your comments, you're still doing something like this:
var results = MyParser(feed_url);
//code that uses results.pubArray
And you cannot do that. Even though you're setting your "pubArray" inside your .get callback, you're trying to use pubArray right after you called MyParser and that's before the .get callback is called.
What you have to do, is call your next step on your program's logic from within the .get callback... that's the only way you can be sure that the pubArray is filled with proper data.
I hope that makes it clearer.
This is because your line
console.log(this.pubArray); // Empty
is being called directly after you issue your Ajax request; it hasn't had time to fetch the data yet. The line
console.log(thisObj.pubArray); // OK
is being called inside the Ajax callback, by which time the data has been fetched.
Thank you all, and particulary #Deleteman .
Here is what I did:
$.get(this.feedUrl, 'xml').success(function () {
thisObj.handleAjax(arguments[0], arguments[1], arguments[2]);
$(document).trigger('MyParserDone');
}).error(function () {
$(document).trigger('MyParserFailed');
});
Then, when i enter "HandleAjax", i'm back in my object context, so "this" refers to my object and the right properties. The only "problem" is that I have to set a listener (MyParserDone) to make sure the parsing is finished.

Categories

Resources