none
Strange behaviour of compiler? RRS feed

  • Question

  • I have this code:

        

    myapp.Articolo.prototype.GetRicavoUn = function (Pezzi) {
        var RicavoAlMinuto = 0.0;  (1)
        try {
            var a = myapp.activeDataWorkspace.ApplicationData.StrutturaProduttivaAttiva().execute().then(function (value) { (2)
                if (value.results.length > 0) {
                    RicavoAlMinuto = value.results[0].RicavoAttesoAlMinuto(); (3)
                }
                else {
                    RicavoAlMinuto = 0; (3bis)
                }
            })
        }
        catch (e) {
            alert(e.description);
        };
        return RicavoAlMinuto; (4)
    };

    I think Italian names are not a problem but if so I can translate.

    This function is called twice by the another form, and this may be ok.

    When I press F8 the debugger goes to step 1, then step 2, then step 4, exit.

    again (for the second call) step 1, step 2, step 4.

    again (I don't know why) step 3, step 3.

    This function does not work for me because initialy does not arrive to step 3 so step 4 returs a value of 0,

    then executes only step 3 and doesn't arrive to step 4.

    Why?

    Thanks

    Tuesday, August 27, 2013 3:24 PM

Answers

  • Hi,

    In JavaScript runtime, data load operations are asynchronous and are represented by promises object. You can see this blog post for more details.

    But the execution flow you described above is expected:

    - (1) is executed.
    - (2) is executed and starts an asynchronous operation with a promise that (3) will be executed when the asynchronous operation succeeds.
    - Because the execution thread is not (and cannot) be paused, (4) is executed and the function completes.
    - At some point later in time, the asynchronous operation succeeds and (3) is executed.

    Best regards,
    Huy

    Wednesday, August 28, 2013 4:55 PM
  • Hi,

    How can I create a public function visible from everywhere?

    This is JavaScript, so it's very easy to do so compared to .NET Framework. If you build your application in Release configuration and look under Bin\Release\HTMLClient\Scripts\Generated, you will find all your user code files for screen/entity simply concatenated to a long file called userCode.js, for example:

    /// <reference path="../GeneratedArtifacts/viewModel.js" />
    
    myapp.Screen1.created = function (screen) {
        // . . .
    };
    
    
    myapp.Screen1.Method3_postRender = function (element, contentItem) {
        // . . .
    };
    
    /// <reference path="../GeneratedArtifacts/viewModel.js" />
    
    myapp.Screen2.created = function (screen) {
        // . . .
    };

    Because there's no encapsulation, when the browser loads this userCode.js file, anything you declare outside of the functions above will become globally visible everywhere. So if you add something like this to a screen user code file:

    /// <reference path="../GeneratedArtifacts/viewModel.js" />
    
    function myAwesomeFunctionThatTheWorldShouldKnow() {
    }
    
    myapp.Browse.created = function (screen) {
        // . . .
    };

    The world will indeed knows about your awesome function.

    But if you're writing many global utilities, the best practice would be:

    • Add all of them into one screen code file (your home screen), and declare them in scope, for example:
    myapp.myUtilities = (function () {
        // Everything inside this function is private,
        // except for things that are assigned to
        // myUtilities (func1, func2).
    
        var myUtilities = {};
    
        myUtilities.func1 = function () { };
        myUtilities.func2 = function () { };
    
        return myUtilities;
    }());
    • Add a reference to the Home screen in the other screen's code file and Intellisense will recognize myapp.myUtilities
    /// <reference path="../GeneratedArtifacts/viewModel.js" />
    /// <reference path="Home.js" />

    If I have well understood there is no way to do that because the function will return the values before the code I need is executed. I think the only solution is place the promise call (.then) every where I need and pass its result to a business function (placed in global scope) to reuse the code.

    Promises can be chained together. I believe if you change your code into

    myapp.Articolo.prototype.GetRicavoUn = function (Pezzi) {
        try {
            return myapp.activeDataWorkspace
                .ApplicationData
                .StrutturaProduttivaAttiva()
                .execute()
                .then(function (value) {
                     var RicavoAlMinuto = 0.0; 
                     if (value.results.length > 0) {
                         RicavoAlMinuto = value.result.[0]
                             .RicavoAttesoAlMinuto();
                     }
                     else {
                         RicavoAlMinuto = 0;
                     }
                     return RicavoAlMinuto;
                });
        }
        catch (e) {
            alert(e.description);
            return null;
        };
    };

    You can then call

    myApp.Articolo
        .GetRicavoUn(aPezzi)
        .then(function(ricavoAlMinuto){
            // ricavoAlMinuto should be the value computed above
        });

    Best regards,
    Huy


    Friday, August 30, 2013 4:12 PM
  • Hi,

    Sorry for the typo, I tend to do that when typing code outside a real Visual Studio project :)

    In this case I believe you're seeing the delay-load behavior that I described above with computing the total amount of Orders for a Customers. getProducts_Components only load the Products_Component entities, not the reference property products_Component.Product. I think using the expand operation in this case should work

    myapp.myUtilities = (function () {
        var myUtilities = {};
    
        myUtilities.GetProductComponentCosts =
        function (product) {
            return product.details.properties
                .Products_Components
                .query.expand("Product")
                .execute()
                .then(function(){
                    return product.Products_Components
                    .sum(function(item){
                        return item.Product.Cost * item.Qty;
                    });
                });
        };
    
        return myUtilities;
    }());

    Note that in the then function above I'm ignoring the result returned from the query and instead use the Products_Components navigation property because I can apply sum() to it. An alternate solution would be

    myapp.myUtilities = (function () {
        var myUtilities = {};
    
        myUtilities.GetProductComponentCosts =
        function (product) {
            return product.details.properties
                .Products_Components
                .query.expand("Product")
                .execute()
                .then(function(result){
                    return msls.iterate(result.results)
                    .sum(function(item) {
                        return item.Product.Cost * item.Qty;
                    });
                });
        };
    
        return myUtilities;
    }());

    As usual, typos may lurk around :)

    Best regards,
    Huy

    • Marked as answer by Morenight Tuesday, September 10, 2013 6:35 PM
    Monday, September 9, 2013 5:09 PM
  • Hi,

    The name used with expand operator should be the navigation property's name. For example if you want to load a collection of Orders and also expand their OrderDetails, because the navigation property on Order class is OrderDetails, you should use .expand("OrderDetails"). In your case you want to load a collection of Products_Components and expand their Products, but the navigation property on Products_Component class is Product, you should use .expand("Product").

    Best regards,
    Huy

    • Marked as answer by Morenight Tuesday, September 10, 2013 8:27 PM
    Tuesday, September 10, 2013 6:44 PM

All replies

  • Hi,

    In JavaScript runtime, data load operations are asynchronous and are represented by promises object. You can see this blog post for more details.

    But the execution flow you described above is expected:

    - (1) is executed.
    - (2) is executed and starts an asynchronous operation with a promise that (3) will be executed when the asynchronous operation succeeds.
    - Because the execution thread is not (and cannot) be paused, (4) is executed and the function completes.
    - At some point later in time, the asynchronous operation succeeds and (3) is executed.

    Best regards,
    Huy

    Wednesday, August 28, 2013 4:55 PM
  • Hi Huy,

    thank you very much and sorry for delay in reply, I resign that nobody would have answered. I think to have understood but this open one new question. That code was in a client property of a table because I wanted call that function from else where. If I have well understood there is no way to do that because the function will return the values before the code I need is executed. I think the only solution is place the promise call (.then) every where I need and pass its result to a business function (placed in global scope) to reuse the code. If this is correct, then new question is: How can I create a public function visible from everywhere?

    Thanks again


    Friday, August 30, 2013 1:22 PM
  • Hi,

    How can I create a public function visible from everywhere?

    This is JavaScript, so it's very easy to do so compared to .NET Framework. If you build your application in Release configuration and look under Bin\Release\HTMLClient\Scripts\Generated, you will find all your user code files for screen/entity simply concatenated to a long file called userCode.js, for example:

    /// <reference path="../GeneratedArtifacts/viewModel.js" />
    
    myapp.Screen1.created = function (screen) {
        // . . .
    };
    
    
    myapp.Screen1.Method3_postRender = function (element, contentItem) {
        // . . .
    };
    
    /// <reference path="../GeneratedArtifacts/viewModel.js" />
    
    myapp.Screen2.created = function (screen) {
        // . . .
    };

    Because there's no encapsulation, when the browser loads this userCode.js file, anything you declare outside of the functions above will become globally visible everywhere. So if you add something like this to a screen user code file:

    /// <reference path="../GeneratedArtifacts/viewModel.js" />
    
    function myAwesomeFunctionThatTheWorldShouldKnow() {
    }
    
    myapp.Browse.created = function (screen) {
        // . . .
    };

    The world will indeed knows about your awesome function.

    But if you're writing many global utilities, the best practice would be:

    • Add all of them into one screen code file (your home screen), and declare them in scope, for example:
    myapp.myUtilities = (function () {
        // Everything inside this function is private,
        // except for things that are assigned to
        // myUtilities (func1, func2).
    
        var myUtilities = {};
    
        myUtilities.func1 = function () { };
        myUtilities.func2 = function () { };
    
        return myUtilities;
    }());
    • Add a reference to the Home screen in the other screen's code file and Intellisense will recognize myapp.myUtilities
    /// <reference path="../GeneratedArtifacts/viewModel.js" />
    /// <reference path="Home.js" />

    If I have well understood there is no way to do that because the function will return the values before the code I need is executed. I think the only solution is place the promise call (.then) every where I need and pass its result to a business function (placed in global scope) to reuse the code.

    Promises can be chained together. I believe if you change your code into

    myapp.Articolo.prototype.GetRicavoUn = function (Pezzi) {
        try {
            return myapp.activeDataWorkspace
                .ApplicationData
                .StrutturaProduttivaAttiva()
                .execute()
                .then(function (value) {
                     var RicavoAlMinuto = 0.0; 
                     if (value.results.length > 0) {
                         RicavoAlMinuto = value.result.[0]
                             .RicavoAttesoAlMinuto();
                     }
                     else {
                         RicavoAlMinuto = 0;
                     }
                     return RicavoAlMinuto;
                });
        }
        catch (e) {
            alert(e.description);
            return null;
        };
    };

    You can then call

    myApp.Articolo
        .GetRicavoUn(aPezzi)
        .then(function(ricavoAlMinuto){
            // ricavoAlMinuto should be the value computed above
        });

    Best regards,
    Huy


    Friday, August 30, 2013 4:12 PM
  • Huy, thank you very much for the extensive reply. Tomorrow I will study and try all your suggestions, but I already know you opened me doors.

    Thank you again

    • Edited by Morenight Friday, August 30, 2013 4:59 PM
    Friday, August 30, 2013 4:56 PM
  • Hi Huy,
    I used successfully your suggestions (BTW thanks again) but I am still in trouble.
    I have read many posts and many tutorials about retrieving data but I think they are focused on UI, I have something more complex. 
    Imagine this tables
    BusinessUnit
    BusinessUnit_Costs

    Products (with a Cost field for simple products)
    Pruducts_Components (for products made up by simple products)

    Orders
    Orders_Details

    In products I have placed
    myapp.Product.prototype.GetComponentCosts = function () {
       return this.Products_Components.sum(function (item) {
            return item.Product.Cost * item.Qty;
       });    
    };
    and in the relative screen a label to display its value using databinding. The same for Business units.
    Everything goes well.
    Problems arrives when in order details I have to propose the price of the item: it has to be calculated  considering the total costs of the active BusinessUnit (I have a  filtered query for this) and other info in BusinessUnit table (TotalCosts/BusinessUnit.ProductionMinutes). Product_Components is even more complex because it has a second reference to products as you can argue in the code above.
    I would like to have a function placed in myApp.myUtilities as suggested which can consume those function too, I would avoid to write a second “myapp.Product.prototype.GetComponentCostsPromise” like function. I have found several examples but when adapting them to my case intellisense doesn't  help: sometimes it doesn't show methods (forEach, then, sum) that in the example are present, sometimes shows me them and at runtime are missing … and I am spending hours and hours to try. 
    Obviously I am not asking the code which I need, I am asking you if you have some links, guidance (may be convenient to use a generic file handler even for this simple logic?), on this particular aspect.
    Thanks
    • Edited by Morenight Tuesday, September 3, 2013 9:17 PM
    Tuesday, September 3, 2013 9:07 PM
  • Hi,

    I would like to have a function placed in myApp.myUtilities as suggested which can consume those function too, I would avoid to write a second “myapp.Product.prototype.GetComponentCostsPromise” like function.

    You can just move the function from the Product prototype to the utilities instead.

    myapp.myUtilities = (function () {
        var myUtilities = {};
    
        myUtilities.GetProductComponentCosts =
        function (product) {
            return product
    .getProducts_Components()
    .then(function(items){ return items.sum(function(item){ return item.Product.Cost * item.Qty; }); }); }; return myUtilities; }());

    Then you can call this anywhere, just instead of using

    product.GetComponentCosts()

    you would use

    myUtilities.getProductComponentCosts(product)

    I have found several examples but when adapting them to my case intellisense doesn't  help: sometimes it doesn't show methods (forEach, then, sum) that in the example are present, sometimes shows me them and at runtime are missing … and I am spending hours and hours to try.

    I'm using Northwind database as the sample for the data concepts below. This MSDN article is also a good read because the concepts - data workspace, data service, entity set, entity object, navigation properties - are the same, only the coding practice is changed in JavaScript because of the asynchronous nature of JavaScript.

    Navigation properties

    At the entity object level, an entity will normally have one navigation property for each association (relationship) it has with another entity object. For example, Order will have 4 navigation properties for its 4 relationships to Customer, Employee, Shipper and Order Detail:

    • Customer
    • Employee
    • Shipper
    • OrderDetails

    The safest way to get a navigation property is to use the getXYZ method, because a navigation property may not be loaded. Intellisense will always show order.getCustomer, order.getOrderDetails and hide order.Customer, order.OrderDetails to promoted this practice. Note that however the property is still there, just hidden.

    There are two types of navigation property:

    • Reference property will return an entity object. For example order.Customer. Usage is simple:
        order.getCustomer().then(function (customer) {
            // It is safe to also access order.Customer
            // in the complete handler.
            //   order.Customer is the same as customer above.
            var companyName = order.Customer.CompanyName;
        });
    • Collection property will return an entity collection. For example order.OrderDetails. This entity collection is the one that has helper methods to enumerate them - first, each, etc
        order.getOrderDetails().then(function (orderDetails) {
            // It is safe to also access order.OrderDetails
            // in the complete handler.
            //   order.OrderDetails is the same as orderDetails above.
            order.OrderDetails.all(function (orderDetail) {
                return orderDetail.Quantity > 10;
            });
            order.OrderDetails.any(function (orderDetail) {
                return orderDetail.Quantity > 10;
            });
            order.OrderDetails.each(function (orderDetail) {
                orderDetail.Quantity += 10;
            });
            order.OrderDetails.first(function (orderDetail) {
                return orderDetail.Quantity > 10;
            });
            order.OrderDetails.sum(function (orderDetail) {
                return orderDetail.Quantity * orderDetail.UnitPrice;
            });
            order.OrderDetails
                .where(function (orderDetail) {
                    return orderDetail.Quantity > 10;
                })
                .sum(function (orderDetail) {
                    return orderDetail.Quantity * orderDetail.UnitPrice;
                });;
        });

    Queries

    This LightSwitch Help Website blog post covers queries in details. But essentially when executing queries you'll get back a result object. The key property of this result object is results, which is an array of entity objects.

    Two basic ways you can work with the asynchronous nature of loading data

    Use the expand operation

    Let's say we want to compute the total amount of Orders for a Customer. This requires loading two navigation property levels: Customer - Orders, then Order - OrderDetails. Instead of

    • customer.getOrders()
    • For each order, order.getOrderDetails(), then wait for all OrderDetails before computing the total.

    We can use the expand operation to load both Orders and their OrderDetails in one query. Here's the code:

        var customer = screen.Customers.selectedItem;
        customer.details.properties.Orders
            .query
            .expand("OrderDetails") // Load Orders, plus OrderDetails
            .execute()
            .then(function (ordersResult) {
                var total = 0;
    
                ordersResult.results.forEach(function (order) {
                    // No async loading here.
                    // All OrderDetails are loaded.
                    order.OrderDetails.each(function (orderDetail) {
                        total += orderDetail.Quantity * parseFloat(orderDetail.UnitPrice, 10);
                    });
                });
    
                alert(total);
            });

    Note how we use the details API to get access to a query that will load Orders for a specific Customer. We only need to wait for one async loading, then all the data we need is there. (This applies to your Product - Product_Components scenario).

    Use WinJS.Promise.join

    In certain scenarios, we still need to wait for multiple asynchronous operations to be done before computing. In that case, WinJS.Promise.join can be used to wait for all promises.

        var promises = {};
        promises.loadCustomer = order.getCustomer();
        promises.loadEmployee = order.getEmployee();
        promises.loadShipper = order.getShipper();
    
        return WinJS.Promise.join(promises).then(function () {
            // At this point, Customer, Employee, Shipper is loaded.
            // ... 
        });

    Hopefully this will apply to most scenarios you have. The main point is to identify the graph of objects you need to load and either expand, or wait on multiple promises before processing.

    Best regards,
    Huy

    Wednesday, September 4, 2013 1:25 AM
  • As usual superb reply. 
    Thanks, thanks, thanks
    Wednesday, September 4, 2013 8:23 AM
  • Huy,
    I am here again because the code you provided seems not to work. I refer to

     myUtilities.GetProductComponentCosts =
        function (product) {
            return product.getProducts_Components
                .then(function(items){
                    return items.sum(function(item){
                        return item.Product.Cost * item.Qty;
                    });
                });
        };

    First of all I had to change in getProducts_Components() otherwise I can not use "then" but this was easy. You suggested to use it as 

    myUtilities.getProductComponentCosts(product).
    In assigment of values (myapp.screen .... = myUtilities.getProductComponentCosts(..product);) it works as expected but only if I change the line                     
    return item.Product.Cost * item.Qty;
    with something like 
    return item.Qty * item.Qty.
    It seems that this happen because when it goes to make the multiplication, Product is not yet loaded so it always returns 0.

    Also using then function as promise call in the screen (myUtilities.getProductComponentCosts(..product).then)
    did not work, and also changing
    return item.Product.Cost * item.Qty;
    in 
    return item.getProduct().then(function(Prod){
       return Prod.Cost * item.Qty;
    });
    because execution in "sum" block exits before reaching    
    return Prod.Cost * item.Qty;
    (initial question of the post)

    Bye
    Saturday, September 7, 2013 5:45 PM
  • Hi,

    Sorry for the typo, I tend to do that when typing code outside a real Visual Studio project :)

    In this case I believe you're seeing the delay-load behavior that I described above with computing the total amount of Orders for a Customers. getProducts_Components only load the Products_Component entities, not the reference property products_Component.Product. I think using the expand operation in this case should work

    myapp.myUtilities = (function () {
        var myUtilities = {};
    
        myUtilities.GetProductComponentCosts =
        function (product) {
            return product.details.properties
                .Products_Components
                .query.expand("Product")
                .execute()
                .then(function(){
                    return product.Products_Components
                    .sum(function(item){
                        return item.Product.Cost * item.Qty;
                    });
                });
        };
    
        return myUtilities;
    }());

    Note that in the then function above I'm ignoring the result returned from the query and instead use the Products_Components navigation property because I can apply sum() to it. An alternate solution would be

    myapp.myUtilities = (function () {
        var myUtilities = {};
    
        myUtilities.GetProductComponentCosts =
        function (product) {
            return product.details.properties
                .Products_Components
                .query.expand("Product")
                .execute()
                .then(function(result){
                    return msls.iterate(result.results)
                    .sum(function(item) {
                        return item.Product.Cost * item.Qty;
                    });
                });
        };
    
        return myUtilities;
    }());

    As usual, typos may lurk around :)

    Best regards,
    Huy

    • Marked as answer by Morenight Tuesday, September 10, 2013 6:35 PM
    Monday, September 9, 2013 5:09 PM
  • Hi,

    I also tried to expand has you did but didn't work. Probably I tried to expand "Products", "Components_Products" and so on instead of singulars or VS got snookered (I did very much trials).

    Now it works!

    Thanks

    Tuesday, September 10, 2013 6:35 PM
  • Hi,

    The name used with expand operator should be the navigation property's name. For example if you want to load a collection of Orders and also expand their OrderDetails, because the navigation property on Order class is OrderDetails, you should use .expand("OrderDetails"). In your case you want to load a collection of Products_Components and expand their Products, but the navigation property on Products_Component class is Product, you should use .expand("Product").

    Best regards,
    Huy

    • Marked as answer by Morenight Tuesday, September 10, 2013 8:27 PM
    Tuesday, September 10, 2013 6:44 PM