1

I've got two pieces of code which may or may not run when my app starts. Both produce a messageDialog, so the second must wait on the first. I'm trying to use promises to do this but I'm having issues with the returned value. Can someone point me in the right direction?

WinJS.Promise.as()
   .then(function () {
       // readTempFile returns a promise, see code below
       if (localSettings.values["lastContent"] != 0) {
            return readTempFile();
       }else{
            // what am I supposed to return here? false? 
       }
   })
   .then(function () {
       // check for release notes
       if (localSettings.values["release"] == null) {
            var updates = "Updates in this version";
            var msg = new Windows.UI.Popups.MessageDialog(updates, "Updates");
            msg.commands.append(new Windows.UI.Popups.UICommand("OK", null, 0));
            msg.showAsync();
       }
   });

function readTempFile(){
    return new WinJS.Promise(function (complete, error, progress) {
        // is my try / catch block redundant here? 
        try {
            tempFolder.getFileAsync("tempFile.txt")
                .then(function (file) {
                    file.openReadAsync().done(function (stream) {
                         // do stuff with the file
                    });
                    var msg = new Windows.UI.Popups.MessageDialog("unsaved work", "Warning");
                    msg.commands.append(new Windows.UI.Popups.UICommand("OK", null, 0));
                    msg.showAsync();
                    complete();
                }, function () {
                    // file not found
                    error();
                });
        }
        catch (e) {
            logError(e);
            error();
        }
    });
}

If both conditions are true, I get an access denied error. As I understand it, readTempFile() returns a promise object which my first then() statement should accept. But I'm not returning anything if the first conditional is met. I don't think that matters in this case as it just falls through to the next then, but it's not good programming.

EDIT:

Amended the readTempFile function to show that it produces a MessageDialog.

roryok
  • 9,325
  • 17
  • 71
  • 138
  • Isn't the `.then` supposed to be single and inside of it to invoke whatever needed? Why 2 ? – kidwon Jul 28 '14 at 14:27
  • @kidwon. I want one to run after the other. I've seen that code in lots of examples. – roryok Jul 28 '14 at 15:59
  • I just noticed that my sample code didn't include a MessageDialog. That's the crucial part here, so I added it in. – roryok Jul 28 '14 at 16:06
  • Note: Here's an example of two MessageDialogs executing one after the other. http://stackoverflow.com/questions/20532134/how-to-print-2-alerts-consecutives-winjs This would be perfect, except that my first block is conditional. It *may not* execute at all – roryok Jul 28 '14 at 16:12

2 Answers2

1

Well, let's try an analogy:

function fetchIfNotCached(){
    if(!cached){
        doSomething();
    }
    // nothing here
}

This is exactly like your case, only asynchronous. Because it utilizes promises you hook on the return value so:

what am I supposed to return here? false?

Anything you want, I'd personally probably just omit it and refactor it to if(!cached) return doSomething() in the .then. Promises chain and compose and you do not need to create them from callback interfaces unless there is a really good reason to do so.

As for readTempFile you're doing a lot of excess work as it looks like getFileAsync already returns a promise. This is a variation of the deferred anti pattern and can be rewritten as:

function(readTempFile){
   return tempFolder.getFileAsync("tempFile.txt").then(function (file) {
      return file.openReadAsync();
   }).then(function (stream) {
      // do stuff with the file, note the return as we wait for it
   });
}
Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • In my case it has to be Synchronous though. The point is, it is currently executing asynchronously, and I'm getting an error. I'm trying to block execution until readTempFile does its thing – roryok Jul 28 '14 at 16:03
  • Why does it "have to be" synchronous? – Benjamin Gruenbaum Jul 28 '14 at 16:04
  • Both pieces of code potentially throw a MessageDialog. If the second block tries to launch a MessageDialog while the first is still up, the app crashes. The second block of code has to wait until the first completes before the space is clear for it to launch its own MessageDialog. NOTE: I left out the MessageDialog code in the example at first, I've fixed that now – roryok Jul 28 '14 at 16:08
  • Does the MessageDialog return a promise? If it does `return` it and it will wait for it. That's how a promise does. – Benjamin Gruenbaum Jul 29 '14 at 08:19
  • I actually needed to put the complete() call inside a done() call attached to the MessageDialog, that did the trick – roryok Jul 29 '14 at 12:02
0

Found the answer. Inside the readTempFile function, I needed to add a done() to the showAsync() of my messageDialog, and put the complete() call in there. Otherwise, complete() was returning the promise while the dialog was still up.

function readTempFile(){
    return new WinJS.Promise(function (complete, error, progress) {
        // is my try / catch block redundant here? 
        try {
            tempFolder.getFileAsync("tempFile.txt")
                .then(function (file) {
                    file.openReadAsync().done(function (stream) {
                         // do stuff with the file
                    });
                    var msg = new Windows.UI.Popups.MessageDialog("unsaved work", "Warning");
                    msg.commands.append(new Windows.UI.Popups.UICommand("OK", null, 0));
                    msg.showAsync().done(function(){
                        // must be inside done(), or it will return prematurely
                        complete();
                    });
                }, function () {
                    // file not found
                    error();
                    // have to add a complete in here too, or the app will 
                    // hang when there's no file                        
                    complete();
                });
        }
        catch (e) {
            logError(e);
            error();
        }
    });
}

After a couple of experiments, I figured that out myself.

As to what needed to return in my empty else statement, it was another WinJS.Promise.as()

WinJS.Promise.as()
   .then(function () {
       // readTempFile returns a promise, see code below
       if (localSettings.values["lastContent"] != 0) {
            return readTempFile();
       }else{
            return WinJS.Promise.as()
       }
   })
   .then(function () {
       // check for release notes
       if (localSettings.values["release"] == null) {
            var updates = "Updates in this version";
            var msg = new Windows.UI.Popups.MessageDialog(updates, "Updates");
            msg.commands.append(new Windows.UI.Popups.UICommand("OK", null, 0));
            msg.showAsync();
       }
   });

I found the answer in a Google Books preview of Beginning Windows Store Application Development – HTML and JavaScript Edition By Scott Isaacs, Kyle Burns in which they showed an otherwise empty else statement returning a WinJS.Promise.as()

roryok
  • 9,325
  • 17
  • 71
  • 138