【问题标题】:WinJS, return a promise from a function which may or may not be asyncWinJS,从一个可能是异步的也可能不是异步的函数返回一个承诺
【发布时间】:2014-01-06 22:21:11
【问题描述】:

我有一种情况,我的 WinJS 应用程序想要调用一个可能是异步的也可能不是异步的函数(例如,在一种情况下,我需要从文件(异步)加载一些数据,但在其他时候我可以从缓存中加载同步)。

查看文档后,我可以将条件逻辑包装在如下承诺中:

A)
return new WinJS.Promise(function() {  // mystuff });

或者可能像这样使用'as':

B)
return WinJS.Promise.as(function() { // mystuff });

问题是,当我调用这个函数时,我是从我的第一页的 ready() 函数中执行的,如下所示:

WinJS.UI.Pages.define("/pages/home/home.html", {

    ready: function () {

         Data.Survey.init().done(function (result) {

            // do some stuff with 'result'

        });
    }
 });

当它写成 'A' 时,它永远不会触发我的 done() 调用。

或者如果我在它写成'B'时调用它,它会在promise 解决之前立即执行我的done() 中的代码。它还从 result 的值看,它只是被设置为我的 init() 函数的内容,而不是被包装在一个 Promise 中。

感觉我在这里做的事情基本上是错误的,但我不确定从哪里开始寻找。

如果有帮助,这是我的 init() 函数的精简版:

    function init() {

    return new WinJS.Promise(function() {

        if (app.context.isFirstRun) {

            app.surveyController.initialiseSurveysAsync().then(function (result) {
                return new WinJS.Binding.List(result.surveys);
            });
        } else {

            var data = app.surveyController.getSurveys();
            return new WinJS.Binding.List(data);
        }
    });
}  

有人对这个有什么想法吗?我不相信“可能异步”是这里的问题,我相信承诺设置没有达到我的预期。任何人都可以在这里看到明显的错误吗?非常感谢任何反馈。

【问题讨论】:

    标签: javascript asynchronous winjs promise


    【解决方案1】:

    一般来说,如果您在完整的 init 例程中执行文件 I/O,则这些 API 会自行返回 Promise,在这种情况下,您希望返回其中一个 Promise 或来自 .then 方法之一的 Promise。

    另一方面,WinJS.Promise.as 旨在将值包装在 Promise 中。但让我更全面地解释一下。

    首先,仔细阅读 WinJS.Promise 构造函数的文档。像许多其他人一样,您错误地假设您只是在 Promise 中包装了一段代码,瞧!它是异步的。不是这种情况。您传递给构造函数的函数是一个initializer,它接收三个参数:一个completeDispatcher 函数、一个errorDispatcher 函数和一个progressDispatcher 函数,我喜欢这样称呼它们。

    对于成功完成、错误完成或报告进度的承诺,初始化程序中的其余代码最终需要调用其中一个调度程序。这些调度程序,在 Promise 中,然后循环并调用已提供给该 Promise 的 then 或 done 方法的任何完成/错误/进度方法。因此,如果您根本不调用调度程序,则没有完成,这正是您所看到的行为。

    使用 WinJS.Promise.as 的相似之处在于它在 Promise 中包装了一个 value。在您的情况下,如果您将一个函数传递给 WinJS.promise.as,您将得到一个由该函数值作为结果实现的承诺。你确实没有获得函数的异步执行。

    要实现异步行为,您必须使用 setTimeout/setInterval(或 Windows 8.1 中的 WinJS 调度程序)在 UI 线程上执行异步工作,或者使用 web worker 作为后台线程并绑定其完成(通过 postMessage)承诺。

    这是一个使用构造函数创建承诺、处理完成、错误和进度情况(以及取消)的完整示例:

        function calculateIntegerSum(max, step) {
        if (max < 1 || step < 1) {
            var err = new WinJS.ErrorFromName("calculateIntegerSum", "max and step must be 1 or greater");
            return WinJS.Promise.wrapError(err);
        }
    
        var _cancel = false;
    
        //The WinJS.Promise constructor's argument is a function that receives 
        //dispatchers for completed, error, and progress cases.
        return new WinJS.Promise(function (completeDispatch, errorDispatch, progressDispatch) {
            var sum = 0;
    
            function iterate(args) {
                for (var i = args.start; i < args.end; i++) {
                    sum += i;
                };
    
                //If for some reason there was an error, create the error with WinJS.ErrorFromName
                //and pass to errorDispatch
                if (false /* replace with any necessary error check -- we don’t have any here */) {
                    errorDispatch(new WinJS.ErrorFromName("calculateIntegerSum", "error occurred"));
                }
    
                if (i >= max) {
                    //Complete--dispatch results to completed handlers
                    completeDispatch(sum);
                } else {
                    //Dispatch intermediate results to progress handlers
                    progressDispatch(sum);
    
                    //Interrupt the operation if canceled
                    if (!_cancel) {
                        setImmediate(iterate, { start: args.end, end: Math.min(args.end + step, max) });
                    }
                }
            }
    
            setImmediate(iterate, { start: 0, end: Math.min(step, max) });
        },
        //Cancellation function 
        function () {
            _cancel = true;
        });
    }
    

    这来自我的免费电子书的附录 A(“揭秘承诺”),用 HTML、CSS 和 JavaScript 编写 Windows 应用商店应用程序,第二版(预览版),请参阅 http://aka.ms/BrockschmidtBook2

    在您的情况下,您可以将数据初始化代码放在迭代函数的位置,并可能从 setImmediate 中调用它。我鼓励您也查看 WinJS 调度程序 API,它可以让您为 UI 线程上的工作设置优先级。

    简而言之,必须了解 new WinJS.PromiseWinJS.Promise.as 本身不会创建异步行为,因为 Promise 本身只是一个调用围绕“稍后交付的结果”的约定与异步没有本质上的关系。

    【讨论】:

    • 感谢 Kraig,这是一个极好的答案。我的假设是我可以将一个函数包装在一个 promise 中而不接收异步功能,但至少接收函数的结果,我现在看到我需要手动调用传入的完整函数。我错过了对 'comp( )' 在 msdn 示例中(它可能被评论为重要的一步!-msdn.microsoft.com/en-us/library/windows/apps/br211867.aspx)。再次感谢您的出色回答。
    • 欢迎您。最初,Promise 并不是最容易掌握的!
    • 几个月后发表评论,只是为了感谢您的解释。但说真的,为什么有人会使用那块 sh**t ?我的意思是,传递一个回调函数更容易,一旦函数完成就会调用它。我看不出花一整天时间理解 promise 是如何工作的,然后花 2 天时间写一个简单的 promise,只是不通过回调。文档说它有助于调试。也许吧,但当你知道自己在做什么时,这毫无意义。我喜欢清晰的东西,清晰的代码,但承诺并不清晰(而且一如既往,文档很烂)
    • 一个简单的回调在简单的场景中会更容易,但事实是,如果有多个操作在进行,异步环境中的编程很快就会变得更加复杂。 Promise 和类似的构造(参见en.wikipedia.org/wiki/Futures_and_promises)允许组合和管理多个操作,这对回调来说是一个完全的痛苦。并且要清楚,这是异步编程的产物,并不特定于 JavaScript、WinJS 或任何特定解决方案,因此您的咆哮在这里被误导了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-07
    • 2022-01-16
    • 2018-12-22
    相关资源
    最近更新 更多