0

I'm working on a small blog engine where the user can create blog entry and possible to link tags to an entry. It is many-to-many relation, but due to that Breeze cannot yet manage this relation I have to expose the join table to breeze so that I can persist the data step-by-step. And my problem is here.

Tables:

  • BlogEntry
  • BlogEntryTag
  • Tag

Scenario:

  • user opens the "new blog entry" form or selects an existing one to be edited
  • enters the text, etc
  • selects one or more tags

Business logic:

  • create a new entity by Breeze / query the selected one
  • save the blog entry (1st server call which gives back the blog_id if the blog entry is new one)
  • check the already existing connections between the tags and blog entry, if the blog entry is edited then the already existing blogEntry-tag relations might change ( 2nd server call)
  • based on the tag name selecting the tag_id from tag table (3rd server call)
  • create the BlogEntrytag entities by breeze
  • persist the BlogEntrytag entities into database ( 4th server call)

I think the order must be consecutive.

I have this code and as you can see the attached screenshot the console logging marked by '_blogEntryEnttity' does not wait until the data returns from the server and it will be executed before the console logging marked by '_blogEntryEnttity inside'. The code will throw a reference exception when it tries to set up the title property a few line later.

var blogEntryEntityQueryPromise = datacontext.blogentry.getById(_blogsObject.id);

                    blogEntryEntityQueryPromise.then(function (result)
                    {
                        console.log('result', result);
                        _blogEntryEntity = result[0];
                        console.log('_blogEntryEnttity inside', _blogEntryEntity);
                        //if I need synchronous execution then I have to put the code here which must be executed consecutively
                    });
                    console.log('_blogEntryEnttity', _blogEntryEntity);
                }

                //mapping the values we got
                _blogEntryEntity.title = _blogsObject.title;
                _blogEntryEntity.leadWithMarkup = _blogsObject.leadWithMarkup;
                _blogEntryEntity.leadWithoutMarkup = _blogsObject.leadWithoutMarkup;
                _blogEntryEntity.bodyWithMarkup = _blogsObject.bodyWithMarkup;
                _blogEntryEntity.bodyWithoutMarkup = _blogsObject.bodyWithoutMarkup;
                console.log('_blogEntryEnttity', _blogEntryEntity);

The example comes from here.

My question is that, why it is not wait until the data comes back? What is the way of handling cases like this?

However, I figured out that, if I need synchronous execution then I should place the code into the success method following the data retrieving from the promise. However, I really don't like this solution because my code will be ugly after a while and hard to maintain.

The datacontext.blogentry.getById looks like below and the implementation is in an abstract class, you can find the code below too. The whole repository pattern comes from John Papa's course on Pluralsight.

Repository class method

function getById(id)
        {

            return this._getById(this.entityName, id);
        }

Abstract repository class method. According to Breeze's documentation page the EntityQuery class' execute method returns a Promise.

function _getById(resource, id) {

            var self = this;
            var manager = self.newManager;
            var Predicate = breeze.Predicate;
            var p1 = new Predicate('id', '==', id);

            return EntityQuery.from(resource)
                .where(p1)
                .using(manager).execute()
                .then(success).catch(_queryFailed);

            function success(data) {
                return data.results;
            }
        }

Execution order

I appreciate your help in advance!

Community
  • 1
  • 1
AndrasCsanyi
  • 3,943
  • 8
  • 45
  • 77
  • What you seem to think is ugly is just how you write asynchronous code in Javascript - there is no waiting of execution for an asynchronous operation. If you use something like promises to help you structure the code it need to be ugly or hard to maintain. – jfriend00 Apr 22 '15 at 21:57

2 Answers2

2

I don't think you need all these round trips. I'd do this:

  1. Query all available Tag entities, so they'll be in the EntityManager's cache (you need these to populate the UI anyway).

  2. If it's an existing BlogEntry, just query the BlogEntry and all its associated BlogEntryTag entities; Breeze will connect the BlogEntryTags to their associated Tags in the cache. You'll add/delete BlogEntryTags if the user selects/unselects Tags for the BlogEntry.

var query = EntityQuery.from("BlogEntries").where("id", "==", id).expand("BlogEntryTags");

  1. If it's a new BlogEntry, it won't have any BlogEntryTags. You'll create these when you save, after the user selects some tags.

  2. Save the added/updated BlogEntry and any added/deleted BlogEntryTag entities to the database in a single saveChanges call.

See the Presenting Many-to-Many doc and its associated plunker for a deeper dive. The UI is different from what you want, but the underlying concepts are useful.

Steve Schmitt
  • 3,124
  • 12
  • 14
  • let me think through, but you may right. There are two sides of this coin. First, I have to learn how to think in this way - parallel execution, promises, etc - and I have to learn the bits and pieces how to use these technics to solve the cases I have. My question in this case rather regarding how to avoid the nested promises. – AndrasCsanyi Apr 23 '15 at 07:05
1

why it is not wait until the data comes back?

Because promises don't magically synchronize execution. They're still asynchronous, they still rely on callbacks.

What is the way of handling cases like this?

You need to put the code that should wait in the then callback.

However, I really don't like this solution because my code will be ugly after a while and hard to maintain.

Not really, you can write concise and elegant asynchronous code with promises. If your code is becoming too much spaghetti, abstract parts of it in own functions. You should be able to get to a clean and flat promise chain.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • thanks for your answer, I really like it! I see that my question wasn't correct, I should have asked about how to avoid nested promises. Thanks for the links and the answers. I'm going to check them. – AndrasCsanyi Apr 23 '15 at 07:06
  • I checked the mentioned articles. You are right! The way how the promises can be chained makes possible to write good and elegant code. – AndrasCsanyi Apr 23 '15 at 19:21