Introduction
Hello again! Today I will talk about the new scheduler API, to manage and schedule tasks or jobs, present in Windows 8.1. This allows us to set the priority of each job so that system resources are more effectively used and responsiveness is improved. The Scheduler API forms part of WinJS.Utilities and is brand new.
Asynchronous Programming
In previous articles, I explained in detail the benefits of Asynchronous programming and what it is. The article can be found here. In case you haven’t read it yet, here is a small quote from it: “Asynchrony is crucial to any app that depends on certain tasks that may cause bottlenecks or block the rest of your program, better yet, freezing. So in fact, it makes applications respond quicker while running several tasks in the background.” Now that we know what Asynchronous programming is all about, let us move on to the new Scheduler API in Windows 8.1.
Scheduler API
The Scheduler API consolidates all work queues into a single queue with priority-based scheduling capabilities. This provides faster and more fluid applications. More details of the Scheduler API can be found here.
Our Project
Open Visual Studio 2013 Preview on Windows 8.1 Preview or Visual Studio 2013 RC on Windows 8.1 RTM, and start a new blank JavaScript Windows Store project entitled Schedule_JS.
“Wait! A JavaScript project? Hannes, are you sure it is not VB?”
“Yes, I am”
“But why?”
“Well, with this article I am trying to explain the Scheduler API, which forms part of WinJS.Utilities, and, it’s time for something different concerning the Windows 8 Store.”
Any other questions, No, right.
Design
Add four buttons to your default.html web page that was created automatically via the toolbox, or you could edit the HTML code directly. Your design should resemble Figure 1, and your code should resemble the following code segment.
default.html Body code :
<body> <p>Scheduling Tasks</p> <button id="pause">Pause Tasks</button> <button id="cancel">Cancel Tasks</button> <button id="yield">Yield Job Within Task</button> <button id="drain">Drain Tasks</button> </body>
Figure 1 – Our Design
Coding
Now that the design is out of the way, we can concentrate on the coding. The first piece of business would be to cover how to schedule jobs and what jobs are. A job is simply a task that wants to run at any given time. This can be anything, but, the whole purpose of job scheduling is so that your app doesn’t freeze when it has to do a lot of work; so the best would be to take time consuming tasks and prioritize them. You should already have an idea of which functions in your programs will take some time and which won’t – you should just set the desired priorities of each job. We schedule the tasks / jobs we want to run via the Scheduler API.
A note: Seeing the fact that this article makes use of JavaScript and HTML coding and references, make sure that you are indeed up to speed with all these technologies.
Scheduling Jobs
Scheduling work is as easy as passing the function that should do the desired task to the schedule method, and specifying the task’s priority. We first need to create the job(s) and then we can set their priorities. We also pass a function to each job so that each job runs separately. This is the beauty of Asynchronous programming. Here is a small example on creating a job:
var JS_Scheduler = WinJS.Utilities.Scheduler; //Create Scheduler Object function JS_Task() { window.output("\nScheduling job"); //Schedule A Job var Job = JS_Scheduler.schedule(function () { window.output("Here I Can Do Whatever I Like"); //Give Some Work }, JS_Scheduler.Priority.normal); //Set Priority }
It looks more complicated than what it is! On the first line, we create the Scheduler object. This gets its capabilities from WinJS.Utilities.Scheduler. Next, we create a function named JS_Task. We then create a job. When and how this job will be run, depends on its priority, which in this case, is Normal. Here is a complete list of available Priorities.
IJob Interface
With the use of the IJob interface, we can Pause, Yield, Resume, Drain and Cancel jobs. Here is more information on it.
Pausing jobs
In your project, add a new JavaScript file by clicking on Project, Add New File and select JavaScript File from the list, as shown in Figure 2, and give it a name such as pause.js.
Figure 2 – Adding a new JavaScript file
Inside Pause.js enter the following code:
(function () { "use strict"; //Are We On Correct WebPage? WinJS.UI.Pages.define("default.html", { ready: function (element, options) { document.getElementById("pause").addEventListener("click", JS_pause, false); //Add Listener WinJS.Navigation.addEventListener("beforenavigate", beforeNavigate); } }); var JS_Scheduler = WinJS.Utilities.Scheduler;//Create Scheduler Object function beforeNavigate(eventInfo) { //Pause Task if (WinJS.Navigation.location === pageLocation) { JS_pause(); } } //Function To Be Run When Cancel Button Is Clicked function JS_pause() { window.output("\nScheduling job to pause"); // Schedule A Job To Be Paused var JobToBePaused = JS_Scheduler.schedule(function () { window.output("Here I Can Do Whatever I Like"); }, JS_Scheduler.Priority.normal); var SomeOtherJob = JS_Scheduler.schedule(function () { window.output("Here I Can Do Some Other Stuff"); }, JS_Scheduler.Priority.normal); window.output("\nI'm Tired Now, May I Take A Break?"); JobToBePaused.pause(); //Pause Job } } )();
A lot happens here! First, we have to determine which page we need this script to run on. In this case, I have kept it at default.html. You could of course have added another HTML file for better organization of your code – but I’ll leave that decision up to you. Then, we have to identify the button we want to use with this JavaScript code. In this case we need the pause button and we need to add a listener to it. A listener is basically the same as an event handler.
I have added another event called beforeNavigate. This event will trigger the pausing code, which is defined in the function named JS_pause.
JS_pause gives output on what we will do, then it creates the scheduler named JobToBePaused. I can let it do whatever I like, but this example just shows a simple message. I set its priority. I then create another job and pause the job I need to. Obviously this is just an example, but I think you’ll get the idea.
Resuming Jobs
To resume a task, you simple need to use:
JobToBePaused.resume
Cancelling Jobs Completely
Add another new JavaScript file to your project, as explained previously, name it cancel.js and enter the following code into it:
(function () { "use strict"; //Are We On Correct WebPage? WinJS.UI.Pages.define("default.html", { ready: function (element, options) { document.getElementById("cancel").addEventListener("click", JS_cancel, false); } }); var JS_Scheduler = WinJS.Utilities.Scheduler; //Create Scheduler Object //Function To Be Run When Cancel Button Is Clicked function JS_cancel() { window.output("\nScheduling job to cancel"); //Schedule A Job To Be Cancelled var JobToBeCanned = JS_Scheduler.schedule(function () { window.output("Here I Can Do Whatever I Like"); //Give Some Work }, JS_Scheduler.Priority.normal); //Set Priority window.output("Had Fun? OK, Good Bye, I am Being Cancelled Now!"); JobToBeCanned.cancel(); //Cancel Job } })();
As you can see, it looks very similar to the previous code segment, we just cancel a job completely.
Yielding Jobs Within a Task
Add a new JavaScript File, name it yield.js and enter the following:
(function () { "use strict"; //Make Sure We Have Correct Page WinJS.UI.Pages.define("default.html", { ready: function (element, options) { document.getElementById("yield").addEventListener("click", JS_yield, false); //Add Listener WinJS.Navigation.addEventListener("beforenavigate", beforeNavigate); } }); var JS_Scheduler = WinJS.Utilities.Scheduler; //New Scheduler Object var Completed; function beforeNavigate(eventInfo) { if (WinJS.Navigation.location === pageLocation) { //Do Necessary Work To Complete All Tasks return; } if (eventInfo.detail.location === pageLocation) { Completed = false; //Task Was Not Completed return; } } function JS_yield() { //Function To Be Run When Yield Clicked JS_Scheduler.schedule(function YieldWorker(YieldJobInfo) { while (!Completed) { if (YieldJobInfo.shouldYield) { window.output("Yielding just temporarily"); //Wait A Bit YieldJobInfo.setWork(YieldWorker); break; } //Run Again else { window.output("Running idle yielding job again"); var start = performance.now(); //Do Complicated Task } } })}});
The trick with yielding jobs within jobs is that you need to know which part of your function might consume most of the processor and time. Once you encounter a possible long task, you could yield it temporarily until something has completed and then resume it again.
Draining Jobs
Add another JavaScript file and name it appropriately. Add the following code to it in order to drain tasks:
(function () { "use strict"; //Right Place? WinJS.UI.Pages.define("default.html", { ready: function (element, options) { document.getElementById("drain").addEventListener("click", JS_drain, false); //Add Listener } }); var JS_Scheduler = WinJS.Utilities.Scheduler; //Function To Run When Drain Button Clicked function JS_drain() { DrainJobBasedOnPriority(JS_Scheduler.Priority.belowNormal, "belowNormal"); } //Function Called By JS_drain To Drain Priority Based function DrainJobBasedOnPriority(JS_Priority, JS_PriorityName) { window.output("\nScheduling job(s) to drain"); //Schedule Job To Not Be Drained JS_Scheduler.schedule(function () { window.output("Normal Priority Job Will Not Be Drained :)"); }, JS_Scheduler.Priority.normal); //Schedule Job To Be Drained JS_Scheduler.schedule(function () { window.output("BelowNormal Priority Will Be Drained :("); }, JS_Scheduler.Priority.belowNormal); //Drain Job window.output("Draining " + JS_PriorityName + " Priorities"); JS_Scheduler.requestDrain(priority).done(function () { window.output("Done draining! I'm Done!"); } ); } })();
What happens here is: we create and schedule jobs like normal. We then request to drain the functions that run under the lowest priority. Why? To clean up resources and it is simply good housekeeping to let things go when they are not really used.
If you were to run your app now, nothing will happen! Why? Because we need to let our default.html file know about all the new JavaScript files we have added. Open default.html and enter the following just above the opening Body tag:
<script src="/js/default.js"></script> <script src="pause.js"></script> <script src="cancel.js"></script> <script src="yield.js"></script> <script src="drain.js"></script>
I am attaching a working sample project with this article, just in case you have missed a step or two.
Conclusion
There you have it! This was very nice and it is just one of the many new wonderful things to expect with VS 2013 and Windows 8.1. Keep an eye out for an upcoming article named New controls and control updates in Windows 8.1 and Visual Studio 2013. Until then, cheers!
About the Author: Hannes du Preez is a Microsoft MVP for Visual Basic for the fifth year in a row. He is a trainer at a South African-based company providing IT training in the Vaal Triangle. You could reach him at hannes [at] ncc-cla [dot] com.