0

有一个代码:

var theContent;    
Windows.Storage.PathIO.readTextAsync(filepath).done(function (fileContent) {

                    theContent= fileContent;

                },

                function (error) {

                });

然后当我想在 Windows.Storage.PathIO.readTextAsync 之外使用“theContent”时,它不起作用...... theContent 变量根本不包含任何内容。

代码的哪一部分是错误的?谢谢!


我放了一些造成麻烦的来源。

global.js,其中包含用于共享的命名空间数组变量。(在 a.js 和 b.js 中)

WinJS.Namespace.define("GLOBAL", {
        theList: null
    });

a.js在某个文件中加载文本。

function readTextFromFiles() {

        GLOBAL.theList= new WinJS.Binding.List();

        for (var i = 0; i < theFileList.length; i++) {

            if (theFileList.getAt(i).filepath !== null) {

Windows.Storage.PathIO.readTextAsync(theFileList.getAt(i).filepath).done(function (fileContent) {

                    var splitted = fileContent.split("\r\n");

                    for (var j = 0; j < splitted.length; j ++) {
                        GLOBAL.theList.push({
                            partA: splitted[j]
                        });
                    }


                },

                function (error) {

                });

            }
        }


    }

b.js以各种方式使用 GLOBAL.theList

ready: function (element, options) {

            new Windows.UI.Popups.MessageDialog("#2 length " + GLOBAL.theList.length, "MESSAGE").showAsync().then();
        },

这是问题所在。 当我调试a.js时,我看到GLOBAL.theList正确包含文件的文本。但是,当我将页面导航到b.html(b.js)时,弹出消息显示“#2 length 0”,这意味着GLOBAL.theList 不包含任何内容。

  • 在页面加载其他 .js 文件之前,我在 default.html 中包含了 global.js。
  • 当我不使用 Windows.Storage.PathIO.... 承诺时,我进行了测试并且它有效。(当我直接将数据任意放入 GLOBAL.theList 时)
4

1 回答 1

3

您要求theContent直线(程序)而不是回调的部分。

.done(function (content) { doStuff(content); });

或者,如果doStuff不使用this并且您不需要对它执行任何其他操作,那么.done(doStuff);当内容返回时,将与内容一起触发 just 和 doStuff。

loadImage = function () {}; // returns promise
showImage = function (image) {} // shows image

var imageLoading = loadImage("img/huge-img.bmp");

imageLoading.done(showImage);

或者为了更简洁,构建showImage使用 Promise(在函数内部订阅它们的回调)。

function showImage(promise) {
    promise.done(function (img) { 
    document.body.appendChild(image);
 });

现在你有

 var image = loadImage("huge-img.bmp");
 showImage(image);

使用承诺。看起来完全自然。

编辑 重新:命名空间


您可能仍然不希望将事物推入列表作为您在回调中所做的唯一事情。

什么在使用列表?

theList.push()一个自定义函数,它具有相同的名称,[].push()但有额外的东西吗?

还是只是一堆东西?

这就是问题所在:
就像 AJAX 回调一样,promise 不会等待事情完成,然后再继续下一件事情。

因此,如果您的程序试图在该数组之外done(当数组最终有数据时)对该数组执行某些操作,那么这些函数将在一个空数组上工作。

相反,您应该使用方法(回调)来处理 Promise 的返回。

// these both do the same thing
/* ... */ .done(function (data) { NAMESPACE.module.callback(data); });
/* ... */ .done(NAMESPACE.Module.callback.bind(NAMESPACE.Module));

// if NAMESPACE.Module.callback ***does not use `this`*** you can write
/* ... */ .done(NAMESPACE.Module.callback);
// if you are unsure for even a second, do one of the other two calls

如果您需要对回调中的数据做更多的事情,那么只需执行以下操作:

/* ... */ .done(function (data) {
    var arr = doStuff(data);

    arr.forEach(function (obj) {
        NAMESPACE.Module.list.push(obj);
    });

    NAMESPACE.Module.callback_relying_on_list();
});

请注意,这里的 2 个键是:

  1. 确保this你的回调是正确的
  2. 在承诺范围内工作——它们的重点是让你不必设置计时器来查看东西是否准备好了,但是......所以如果你只是设置值而不告诉任何人他们已经准备好了准备好了,承诺没有发挥作用

编辑#2 回复:来源


看看你在这里做什么(简化):

// a.js

function getFiles () {
    GLOBAL.list = [];
    getAsyncData().done(function (text) {
        GLOBAL.list.push({ content : text });
    });
}

//b.js
GLOBAL.Messages = {
    write : function (messages) {
        messages.forEach(/* ... */);
    },
    ready : function () { alert(GLOBAL.list.length); } }
};

// main program
getFiles();
GLOBAL.Messages.ready();
GLOBAL.Messages.write(GLOBAL.list);

我知道这不是您的确切代码,但请看一下简化的情况:

如果我的getFiles功能完美运行,但服务器在 30 秒内没有发回我的数据,会发生什么情况?

我的readywrite函数不会等待这种情况发生。我一做完
他们就会开火。getFiles

所以在这里,你在错误的地方得到了这个承诺。
这是 100% 正确的方法,但为什么不这样做:

// a.js
function getFiles () {
    var filesLoaded = getAsyncData(); // I'm collecting the promise, not chaining it. 
    // again, simplified
    filesLoaded.done(function (text) { GLOBAL.list.push({ content : text}); });

    // ***super important part***
    return filesLoaded;
}


// b.js
/* ... */ = {

    ready : function (promise) {
        promise.done(function () { alert(GLOBAL.list.length); });
    },

    write : function (promise) {
        promise.done(/* write the list */);
    }
}

现在你的主要看起来像这样:

var files = getFiles(); // files is the promise
// files.done, files.then, etc

GLOBAL.Messages.ready(files); // giving the promise to the method
// ready(promise) will subscribe to promise.done

GLOBAL.messages.write(files); // so will write(promise);

以这种方式使用 Promise 时,请记住,我的示例与您添加到链中的 files对象完全相同。.done()

还要记住,有时你的函数会关心 promise 返回的值,而其他时候,函数只想知道 promise 何时完成,因此它们可以做一些事后需要发生的事情,而不是设置计时器来检查.

并且在您处理同步数据的任何时候都可以使用已清理的功能,现在您可以:

var files = readTextFromFiles();
/* ... */.ready(promise);

这是否意味着您需要一个更多的功能来介于它ready正在做什么和您的旧准备看起来像之间?

嗯,是的……

但是值得知道您的功能不会过早关闭。
它看起来非常干净,没有巢穴,同时仍然是 100% 同步的。

如果你赶时间,或者你不能编辑那些其他功能,你可能会做一个坏主意,那就是在页面底部引导一些东西:

(function (promise) { promise.done(function () {
    /* ... everything() ... */ });
}(files));

丑陋且难以区分,需要在单独的页面上进行编辑,但至少它仍然是异步的,它仍然按顺序做事。

但这仍然意味着返回、收集和传递承诺。

希望有帮助。

于 2013-01-06T21:00:13.870 回答