In this post I’m going to talk about WinJS.xhr that makes an XMLHttpRequest as a Promise, and how to save and load images and from your local storage.
What is a Promise?
Promise is a way for Asynchronous Programming in JavaScript, Avoiding synchronous execution in single-threaded languages like JavaScript is necessary in order to create apps that are responsive and high performing. Windows Library for JavaScript provides a consistent and predictable mechanism called a Promise that simplifies asynchronous programming.
A promise implements a method for registering callbacks for state change notifications, named then.
Instead of writing a single get action that force your code to wait for response.
var result = myWebService.get(http://www.contoso.com);
Or if you think about writing more code like this:
myWebService.addEventListener('completed', function(result)
{ /* do something */});
myWebService.get(http://www.contoso.com);
You should use WinJS Promise to create Asynchronous action using then method.
myWebService.get("http://www.contoso.com")
.then(
function (result) { /* do something */ },
function (error) { /* handle error */ },
function (progress) { /* report progress */ }
);
For this post I wrote a simple JavaScript Metro App that downloads web images and saves them to local storage and a gallery that display all images under App local storage.

Download Demo Project
Step 1: Create a new Windows 8 JavaScript Metro App
First create empty JavaScript project and add new js file called - imgDownloader
We will use this file to download and locate the local images.
Step 2: Asynchronous Image Download
We need to download the image asynchronously, then save the stream we get to a local file.
The first thing is the folder location, Each application has three available folders under - Windows.Storage.ApplicationData.current - to save user data:
Now using WinJS.xhr we enter the image url and define the response type as “Blob”, again this is an Asynchronous method and we can use THEN in order to register a callback that will called when the request is finished.
Once we get the result from Xhr we use the folder object to create new file with the name the user passed, again once the new file was created we open this file for editing and get the stream.
Code Flow:
- Using Ajax get the image = WinJS.xhr({ url: imgUrl, responseType: "blob" }).then
- After we received the image we create a new file = folder.createFileAsync(imgName,..).then
- Open our file for edit = file.openAsync(Windows.Storage.FileAccessMode.readWrite).then
- Copy the image content = copyAsync(blob.msDetachStream(), stream).then
- Close stream = stream.flushAsync().then
function download(imgUrl, imgName) {
return WinJS.xhr({ url: imgUrl, responseType: "blob" }).then(function
(result) {
var blob = result.response;
return folder.createFileAsync(imgName, Windows.Storage.
CreationCollisionOption.replaceExisting).then(function (file){
// Open the returned file in order to copy the data
return file.openAsync(Windows.Storage.FileAccessMode.readWrite).
then(function (stream) {
return Windows.Storage.Streams.RandomAccessStream.copyAsync
(blob.msDetachStream(), stream).then(function () {
// Copy the stream from the blob to the File stream
return stream.flushAsync().then(function () {
stream.close();
});
});
});
});
}, function (e) {
var msg = new Windows.UI.Popups.MessageDialog(e.message);
msg.showAsync();
});
}
Step 3: Locate Local File
After the download has completed we want to locate the local file we just saved and return the file object, using the file object we can get the file type ,creation date and more.
Using Windows.Storage.ApplicationData.current.local.getFileAsync (or Temp, Roaming") , we can search for a specific file under that folder, if the file is found in the local folder we return the file, else return null. (File Not Found)
function fileExists(fileName) {
return folder.getFileAsync(fileName).then(function (file) {
return file;
}, function (err) {
return null;
});
}
Step 4: Add imgDownloader Namespace
In order to call these methods from Default.js we need to add the namespace using the following methods:
WinJS.Namespace.define('imgDownloader', {
download: download,
fileExists: fileExists
});
Step 5: Add page functionality
Now, when the user writes the image Uri and clicks the “Get Image” button we’ll call the getImage function.
app.onactivated = function (eventObject) {
if (eventObject.detail.kind === Windows.ApplicationModel.Activation.
ActivationKind.launch) {
// TODO: Initialize your application here.
WinJS.UI.processAll();
document.querySelector("#btnDownloadImg").addEventListener("click"
, function () {
getImage();
});
};
using querySelector we’ll take the Uri and File Name values, using the imgDownloader namespace and call Download function using the user supplied values. Because the download function has a callback value we can use the then method.
After the download operation has completed we call the fileExists function to get the local file object.
After getting the Image we demonstrate two options for displaying the image:
- ms-appdata:// protocol – Path To Local Folder
- URL.createObjectURL – Converting the file to blob, you can choose to create permanent blob so you can use it again.
function getImage() {
var imgUrl = document.querySelector("#txtUrl").value;
var fileName = document.querySelector("#txtFileName").value;
imgDownloader.download(imgUrl,
fileName).then(function () {
imgDownloader.fileExists(fileName).then(function (file) {
document.querySelector("#mainImg").src =
URL.createObjectURL(file, false);
// using the ms-appdata:// protocol.
document.querySelector("#mainImg2").src =
"ms-appdata:///Local/" + fileName;
document.querySelector("#filePath").textContent =
"Path: " + file.path;
document.querySelector("#fileType").textContent =
"Display Type: " + file.displayType;
document.querySelector("#dateCreated").textContent =
"Date Created: " + file.dateCreated;
drawGallery();
}, function (err) {
var msg = new Windows.UI.Popups.MessageDialog
("Picture Not Found");
msg.showAsync();
});
});
}
The last thing I want to do is locate all files under my local folder and display all image files.
Again we’ll use Windows.Storage.ApplicationData.current.localFolder but now let’s call “getItemsAsync” to get all files, then forEach over these items and make sure to handle only images, convert each file to a blob using createObjectURL and add the image to our gallery div.
function drawGallery() {
Windows.Storage.ApplicationData.current.localFolder.getItemsAsync().
then(function (items) {
var div = document.querySelector("#existingFiles");
div.textContent = "";
items.forEach(function (storageItem) {
if (storageItem.fileType === ".png" ||
storageItem.fileType === ".jpg" ||
storageItem.fileType === ".jpeg") {
var image = document.createElement("img");
image.style.width = "100px";
image.style.height = "100px";
image.src = URL.createObjectURL(storageItem);
image.alt = image.src;
div.appendChild(image);
}
}, function (e) {
var msg = new Windows.UI.Popups.MessageDialog(e);
msg.showAsync();
});
});
}
Download Demo Project