With the changes that Microsoft has been making, we are seeing more and more signs that we need to adapt and write code that is both efficient and easy to read. We can include all the comments we can in the code, but if it's a garbled mess it won't do any good. Writing client-side scripts for Dynamics 365 can be done in a multitude of ways since the language is super relaxed and doesn't really care how you write it, as long as it works.
In order to make our code nicer for both consultants and clients, we have been implementing asynchronous calls whenever possible so the interface does not get locked down as the process completes. Microsoft has included some of these asynchronous calls by default using the Web API and promises. Promises are a way we can encapsulate a block of code and essentially wait for it to complete before moving on. This is beneficial when we have a variety of processes that run in an asynchronous format but need to be completed in order. If we didn't use promises, the code could execute at different times and variables could not be accessible in subsequent functions, but there's an easy way to make sure our code is clean and executes in order - promise chaining.
We can chain promises together in order execute functions in order, but passing around input and output variables can be complicated if not done properly. Anytime I personally write code, I try to work with what I consider a data object. This data object can be updated with outputs from promises and passed to subsequent functions to be accessed with declaring any new variables, local or global.
In the screenshot below, we can see just how clean the chaining of the promises can be, and if your naming conventions of the functions are clear enough it can almost be read in plain English.
To explain, this function creates invoices from a ribbon button on the Membership Period entity that we have in Dynamics 365. It accepts a parameter called ctx which is the form context parameter passed in from the Ribbon Workbench. As you can see, we declare a data object as a Javascript object, and then assign the Invoices property to an empty array and the FormContext property to the form context passed in (to be accessed anywhere in the process). After showing a blocking message to the user (so they can wait until the process is complete) we execute the promises in the order they need to happen.
The first step is to get the Membership Period record, passing in the data object we created above. After the Membership Period record is retrieved, we then get the Orders for the specific Membership Period. You can see that we don't need to pass in any parameters to the next promise because it accepts the data object as its first parameter. This makes the functions very easy to read and not convoluted with unnecessary variable declarations.
If we look at the GetOrdersForMembershipPeriod it accepts the data object parameter and executes its code wrapped in a promise. When we execute our code, we validate the result and add the retrieved results to the data.RelatedOrders property. You may also have noticed that when we execute the request we use the data.FormContext property. This is available to use through the data object because we set it initially prior to calling this function. After the related orders are set, the function is resolved with the data object again, which can then be passed into the next function.
In this function, we iterate through each of the related orders that were retrieved in the function above, and execute a promise on all records. This function splices the orders up into batches of 4 and runs the CreateInvoice function on each, passing in both the order reference and the data object. The benefit of this is now each CreateInvoice function that gets called will have a reference to the Order, the FormContext, and any other properties that were bound prior to it being called.
As you can see, utilizing a data object to read and write properties to allows you to clean up your code immensely and make it that much easier to read. From a performance stand-point, you are only dealing with a single variable rather than individual variables for input/output in the functions, so in theory this would run faster than if you didn't use a data object.
Looking back at the first image, since we are promise chaining we can actually catch ANY error that may be presented in ANY of the promises that are chained together. This is beneficial because then you are not writing catch statements for each promise. Utilizing these methods, we have written clean, legible code for chaining of about 10 or so functions (if not more) with all of them executing in order and passing around the same object with different properties set.
Commentaires