Search code examples
javascriptjquerywindowsfunctionwinjs

winJS return var from function undefined


I want get a image from my local harddrive. My function returns undefined.

function getImageFromLocal() {
    var imageurl = 0;
    return new WinJS.Promise(function () {
        picturesLib.getItemAsync("APP Folder X").then(function (appfolder) {
            appfolder.getItemAsync("Subfolder X").then(function (file) {
                file.getItemAsync("Image.jpg").done(function (image) {
                    imageurl = URL.createObjectURL(image);
                });
            });
        });
    });
  return imageurl;
}

Solution

  • When working with asynchronous API calls like StorageFolder.getItemAsync, it doesn't work to assign the ultimate result to a local variable and attempt to return that.

    In your code, moreover, you have two return statements, the first of which returns a promise inside which is wrapped the return value from a chain of promises ending in .done, which is what's giving you undefined. The second return statement is never reached, and even if it were, would return the initial value of 0 because by that time the async code won't have executed.

    I'll point out further that using new WinJS.Promise isn't at all what you want here, but I won't belabor the details. If you really want the full story on promises and how they work, refer to my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition, both in Chapter 3 (basics) and Appendix A (full story).

    Back to your code, I'm not entirely sure what you're trying to do. With your function named with "Local" you suggest you're trying to retrieve an image from your app data, but the use of a variable called picturesLib suggests you're coming from the Pictures library. Let me address each of those separately.

    First, if you're working with local appdata, you can bypass the entire process you've shown here by simply using a URI in the form ms-appdata:///local/folder1/folder2/image.jpg. You can assign such a URI directly to an img.src attribute and it'll just work. This will work synchronously, and also works for ms-appx:/// URIs that refer to in-package resources.

    Second, when working with images you cannot reference so conveniently with a URI, you need to get its StorageFile and pass that to URL.createObjectURL, the result of which you can assign to an img.src as well, as I think you know. To save some trouble, though, you can use a full relative path with StorageFile.getFileAsync. This way you avoid having to write a promise chain to navigate a folder structure and reduce all that to a single call. That is, if pictureLib is the StorageFolder from Windows.Storage.KnownFolders.picturesLibrary, then you can just use:

     picturesLib.getFileAsync("App folder X\subfolder x\image.jpg");
    

    Now for your original function, whose purpose is to return a URI for that image, it's necessary that it returns a promise itself, and that the caller attached a .done to that promise to obtain the result. You do this properly by returning the value you want inside the completed handler for getFileAsync, but be sure to use .then which returns a promise for that return value (.done will return undefined).

    function getPicturesLibUriAsync(relativePath) {
        return picturesLib.getFileAsync(relativePath).then(function (file) {
            return URL.createObjectURL(file);
        });
    }
    

    Then you call the method like this, using .done to get the async result:

    getPicturesLibUriAsync("folder1\folder2\image1.jpg").done(function (uri) {
        someImageElement.src = uri;
    });
    

    Again, refer to my book for full details on how promises work, especially using the return value of .then.