Search code examples
file-iopromisewinjs

WinJS how to make appendTextAsync wait for file available


I have code like this for logging in my WinJS app:

function setupFileLog() {
    var logFn = function (message, tag, type) {
        if (!log_file) return; // log_file is global, setup below with a StorageFile object
        Windows.Storage.FileIO.appendTextAsync(log_file, tag + ' ' + type + ': ' + message)
            .done(null, function (error) {
                // I have a breakpoint here to catch the error
                var msg = error.detail.message;
                }
            });
    };

    app_folder.createFolderAsync('logs', Windows.Storage.CreationCollisionOption.openIfExists)
       .then(function (logfolder) {
           var now = new Date();
           var logfilename = config.device + "_" + now.toDateString() + ".log";
           return logfolder.createFileAsync(logfilename, Windows.Storage.CreationCollisionOption.openIfExists)
       })
       .done(function (file) {
           log_file = file; // save in global var
           WinJS.Utilities.startLog({ tags: "myApp", action: logFn });
       });
}

setupFileLog();

// ...
// do stuff and log things
// ...

WinJS.log && WinJS.log('I did some stuff', 'myApp','info');
WinJS.log && WinJS.log('I got an error: '+error, 'myApp','error');

Intermittently, I get an error in my logFn "The process cannot access the file because it is in use by another process" or "Access is denied". I think that multiple calls to WinJS.log are colliding because of the async nature of the appendText call and trying to get a handle to the log file before the previous logging call is finished writing.

Can I make appendTextAsync wait until the log file is not in use? I can't find any calls to check if the file is busy. I think there is a way to make appendTextAsync act in a synchronous way but I'd like to avoid doing that in all cases since this is just an intermittent case.


Solution

  • You could make a chain of calls that is automatically executed in sequence:

    function setupFileLog() {
        var log_file = app_folder.createFolderAsync('logs', Windows.Storage.CreationCollisionOption.openIfExists)
       .then(function (logfolder) {
            var now = new Date();
            var logfilename = config.device + "_" + now.toDateString() + ".log";
            return logfolder.createFileAsync(logfilename, Windows.Storage.CreationCollisionOption.openIfExists)
        });
    
        WinJS.Utilities.startLog({
            tags: "myApp",
            action: function log_fn(message, tag, type) {
                 log_file = log_file.then(function(file) {
                     return Windows.Storage.FileIO.appendTextAsync(file, tag + ' ' + type + ': ' + message)
                     .then(function() {
                         return file;
                     }, function (error) {
                        // I have a breakpoint here to catch the error
                        var msg = error.detail.message;
                        return file; // for further logs, or
                        throw error; // go stop logging
                     });
                });
            }
        });
    }
    

    Every call to the log_fn does chain another appendTextAsync call onto the log_file promise. The bonus is that calls are already chained when the log file is not yet created, instead of simply dropping log lines.