As you are thinking more about your Web page being an app, you look for ways to reduce the complexity by using modules. In earlier post Getting Started with Modules Using RequireJS , you learned how RequireJS provides a great way to think of your app in modules and to asynchronously load and run your app.
RequireJS helps your describe the dependencies of a module and make sure you load them before executing your script.
But what happens when your module is long running? You can certainly turn that portion into a module and the require the completion before continuing. But in my case, I want think about my AMD module as an object and then call long-running methods on that module after it has been loaded.
This snippet expands on Asynchronous JavaScript Promises Using Q and shows how you can use a promise inside your module that will have some long running asynchronous method.
When you make an asynchronous call, you can use a promise to handle both successful completion of the work and potential errors that may arise during execution. Upon the successful completion of one asynchronous call, you may want to pass the result to make another request.
The solution combines the promises of Q.js with the Asynchronous Module Definition (AMD) of Require.JS.
Getting Started
Get Q and RequireJS from their websites or through NuGet in your Visual Studio project.
In this application, you will need:
- HTML file
- Scripts folder with two files: q.js and require.js
- Scripts/app folder with files: main.js and sampleQ.js
Load JavaScript files
In the first step, create an HTML page that loads require.js and starts the main.js file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Require + Q</title> | |
</head> | |
<body> | |
<script data-main="Scripts/app/main" src="scripts/require.js"></script> | |
<script> | |
console.log("on home page"); | |
</script> | |
</body> | |
</html> |
Scripts/app/main.js
Create a main.js file that sets up the libraries that you will need. Use require.confg() to point to where to load the JavaScript libraries.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ====== set up require.js ================ | |
(function () { | |
"use strict"; | |
require.config({ | |
baseUrl: 'Scripts', | |
paths: { | |
// "underscore": "lodash", | |
// "jquery": "jquery-2.0.3", | |
"q": "q" | |
} | |
}); | |
})(); |
You’ll come back to main.js to call your module in a later step.
Create Your Module
Create a module in Scripts/app/sampleQ.js. Define your module. In this case, you’ll define it as a function. In the example, you canuse the CommonJS syntax. If there are a lot of requirements, this syntax can be used to help you not get the parameters confused.
Then create a private method that returns a promise using Q’s deferreds.. If successful, it uses the Q’s resolve method. If unsuccessful, it uses Q’s reject method.
Finally, the module returns the methods that are publicly accessible.
Scripts/app/sampleQ.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define(function (require) { | |
"use strict"; | |
// using simplified CommonJS syntax so it is clear what vars I can use in this | |
// function and not to confuse the order of them | |
var Q = require('q'); | |
// Add other libraries such a: var amplify = require('amplify'); | |
// a private method that delays | |
var count = function (beginningNumber, endingNumber) { | |
var deferral = Q.defer(); | |
var newNumber = 0; | |
if (endingNumber < beginningNumber) { | |
deferral.reject("endingNumber before beginningNumber"); | |
} | |
// do something that takes some time | |
for (var i = beginningNumber; i < endingNumber; i++) { | |
Q.delay(10); | |
newNumber += i; | |
} | |
console.log("in sampleQ: " + newNumber + typeof(newNumber)); | |
deferral.resolve(newNumber); | |
return deferral.promise; | |
}; return { | |
count: count | |
}; | |
}); |
Now I can call the count method in the module.
Calling the Module Using Promises
Now you can call into the module, which requires the module and q and any other libraries needed by the module. You can then call the publicly accessible methods. When they return a promise, you can handle the results for success or errors.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require(['app/sampleQ', 'q'], function (sample, Q) { | |
// do something with the sample object | |
sample.count(10, 20).then(function (countResponse) { | |
console.log("count successful" + countResponse); | |
}); | |
// catch an error | |
sample.count(20, 10).then(function (countResponse) { | |
console.log("count successful" + countResponse); | |
}).catch(function (error) { | |
console.log("error in sample: " + error); | |
}); | |
}); |
Sample Code
Sample code for this post is available in the DevDays GitHub repository at: https://github.com/devdays/single-page-app/tree/master/LoadTemplates
See the samples at 4a-RequirePlusQTest.html, Scripts/app/SampleQ.js, and Scripts/app/samplemain.js.