【问题标题】:use a variable outside of Promise (in windows8 javascript development)使用 Promise 之外的变量(在 windows8 javascript 开发中)
【发布时间】:2013-01-08 23:53:51
【问题描述】:

有一个代码:

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 时)

【问题讨论】:

  • Klados,你做错了什么期望fileContent.done() 表达式执行后立即出现。此时,...readTextAsync() 还没有返回任何数据;它返回了一个 promise 的数据。数据将在短时间内异步到达。因此,您对数据的处理必须在.done() 处理程序内部。为异步派生的数据设置外部变量(例如 theContent)通常没有什么意义。

标签: javascript windows-8 promise


【解决方案1】:

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

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

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

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

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

imageLoading.done(showImage);

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

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();
});

注意这里的两个键是:

  1. 确保this 在您的回调中正确
  2. 在 Promise 内工作——这样做的目的是让您不必设置计时器来查看是否准备好,但是...所以如果您只是设置值而不告诉任何人他们准备好了,但承诺没有发挥作用

编辑 #2 re: 来源


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

// 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 */);
    }
}

现在你的 main 看起来像这样:

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));

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

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

希望对您有所帮助。

【讨论】:

  • 谢谢!但我有一个问题,我可以在 .done() 表达式中使用命名空间变量吗?我的意思是将 'theContent=fileContent' 替换为 'MyNamespace.theList.push ({title:fileContent}); ?因为即使“fileContent”填充了正确的数据,我也无法使用 MyNamespace.theList。似乎是空的。 @Norguard
  • 看看,如果有什么遗漏,请告诉我,你需要知道的。
  • 我认为我错过了问题的重点。我放了部分来源和描述。感谢您的友好回复。 @Norguard
  • @klados 看看编辑#2。您的问题看起来仍然像我之前提到的,只是稍微复杂一些。完全相同的解决方案仍然适用。如果还有问题,请告诉我。
  • 我又卡住了...实际上我已经尝试了您的建议,但我认为我做错了什么,一个大问题。简单地说,我只想'读取文件中的文本(在 a.js 中)'->'将文本存储在某个 GLOBAL 变量中(在 global.js 中)'->'在 a.js 中使用存储的变量,b。 js等..'但在这里,我无法理解'GLOBAL.messages.ready'并写。 “写”部分是做什么的?什么是“主程序”?我的代码中没有这样的东西。只有 a.js、b.js、global.js。 a.js 具有从文件中读取文本的功能,并且 a.js 和 b.js 都应该使用存储的文本。谢谢你..真的很多!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-14
  • 1970-01-01
  • 2017-09-25
  • 1970-01-01
  • 1970-01-01
  • 2012-04-13
  • 2018-05-13
相关资源
最近更新 更多