【问题标题】:return a boolean after promise has been resolved/rejected?承诺被解决/拒绝后返回布尔值?
【发布时间】:2015-10-20 07:12:56
【问题描述】:

我有一个运行良好的功能。我只想根据承诺返回真或假。

 //I want this function to return a simple true or false!!!
function isAppOnline() {
            var is_connected = connectivityMonitor.isInternetConnected();
            is_connected.then(function(result) {
                console.log('INTERNET_CHECK_API : app online');//works fine
                return true;//this is not being returned
            }, function(error) {
                console.log('INTERNET_CHECK_API : app offline');//works fine
                return false;//this is not being returned
            });
        }

但是当我调用这个函数时,

is_online = isAppOnline();

is_online 总是 undefined 。为什么函数不能返回简单的布尔值?

更新:

这就是我想要做的: 我只是想打开一个弹出窗口,通知用户他离线。我在 10 秒后定期调用函数 isAppOnline。这个功能已经在我的工厂中使用了一个承诺。我不想让事情变得过于复杂,但对我来说很重要的是这个函数返回一个布尔值,所以基于此,我可以采取相应的行动。

【问题讨论】:

  • 根据您的编辑,您可以将此弹出逻辑剥离到它自己的控制器中,无论如何这可能是个好主意。该控制器可以与页面之间的其他静态内容一起放置,您的导航是否在其自己的控制器中,它可以在应用程序中处于与此类似的级别?那么你只需要在一个地方使用 Promise,而且它更加模块化。唯一值得关注的原因。也使它更可测试。你甚至可以把它放在一个指令中,然后把它放在你想要的地方。
  • 此代码适用于所有控制器。 :)

标签: angularjs ionic-framework ionic angular-promise


【解决方案1】:

编辑:对于 ES2017 如果您是使用 ES2017 的幸运者之一,那么您可以使用新的 await/async 关键字。它们非常出色,允许您编写读取同步的异步代码。 (它仍然是引擎盖下的承诺,只是将它们拆箱)。

function isOnline() {
    return Promise.resolve(true);
}

async function Main() {
    const online = await isOnline();

  console.log(online);
}

Main();

fidle


因为它是异步的。您的 isAppOnline 方法在您的承诺解决之前返回。

我认为正在进行某种形式的 AJAX 调用来检查网络连接,因此它必须等待它响应。 JavaScript 是单线程的,如果该线程锁定等待该请求响应以便它可能是同步的,那么您的整个 JavaScript 将暂停直到它返回。不好。

因此,如果您希望isAppOnline 的调用者知道结果,您必须选择。要么给它一个回调,要么返回承诺(更好的选择)

function isAppOnline(cb) {
    var is_connected = connectivityMonitor.isInternetConnected();
    is_connected.then(function(result) {
        console.log('INTERNET_CHECK_API : app online');//works fine
        cb(true);
    }, function(error) {
        console.log('INTERNET_CHECK_API : app offline');//works fine
        cb(false);
    });
}

//better option
function isAppOnline() {
    return connectivityMonitor.isInternetConnected().then(function(result) {
        console.log('INTERNET_CHECK_API : app online');//works fine
        return true;
    }, function(error) {
        console.log('INTERNET_CHECK_API : app offline');//works fine
        return false;
    });
}

//used as
isAppOnline().then(function (isOnline) {
    console.log('Is it online?', isOnline);
});

【讨论】:

  • 哦,第二个?我应该在没有看到你的其余代码的情况下添加这个特定的实现是未经测试的。但是原理很好,我几乎在我工作过的每一个角度应用程序中都亲自使用过这种方法。从使用 .then() 的方法返回 Promise 是完全可链接的。
  • 是的,第二个。好吧,我一直在寻找更简单的东西。我知道我可以再添加一个承诺,但这就是我问这个问题的原因。一个又一个的承诺……没有更简单的东西。无论如何谢谢。
  • @Jayesh Jain 注意在 is_connected 之前添加的返回
  • @lcycool 答案已编辑。杰伊什贾恩。如果您必须在嵌套的 Promise 链中一个又一个 Promise 进行操作,而这些 Promise 最初是从尚未创建的 Promise 中链接起来的,您可能需要重新考虑您的应用程序结构。
  • 好吧,这只是我工厂的一个承诺......然后是我的 .run() 中的 isAppOnline() 。所以有两个承诺。我不认为我的结构不好,我可能需要在多个地方使用相同的功能。
【解决方案2】:

Promise 与简单语句的工作方式不同,因为它们可能稍后返回,因此您可能需要重新考虑程序流程以处理不同的初始和最终状态。

function isAppOnline() {
    var is_connected = connectivityMonitor.isInternetConnected();
    return is_connected.then(function(result) { // add return here to return the whole promise
        console.log('INTERNET_CHECK_API : app online');//works fine
        return; //this will resolve the promise returned
    }, function(error) {
        console.log('INTERNET_CHECK_API : app offline');//works fine
        $q.reject(); // this will reject the promise returned
    });
}

您将不得不在调用控制器中处理不断变化的状态(它可能会暂时为假,然后转为真,等等)。您可以考虑使用加载状态来防止误导用户。

is_online = false;
isLoading = true;

isAppOnline().then(function() {
    is_online = true;
}, function() {
    is_online = false;
})
.finally(function(){
    isLoading = false;
});

【讨论】:

    【解决方案3】:

    您的问题表明需要进一步探索 Javascript 的异步处理。我推荐阅读:How do I return the response from an asynchronous call?

    您可以实施一些技巧来强制方法同步返回,但 Angular 的构建方式几乎可以在任何地方兑现承诺。如果您能给我们更多关于您要完成的工作的背景信息,我们或许可以帮助您以利用 Promise 本身的方式编写代码。

    【讨论】:

    • 当然...我会再补充几点来解释我想要实现的目标。
    【解决方案4】:

    您可以尝试以下方法(这是未经测试的代码,因为您没有提供工作脚本):

    function isAppOnline() {
        var defer = $q.defer();
    
        var is_connected = connectivityMonitor.isInternetConnected();
        is_connected.then(function(result) {
            console.log('INTERNET_CHECK_API : app online');//works fine
            defer.resolve(true);
        }, function(error) {
            console.log('INTERNET_CHECK_API : app offline');//works fine
            defer.resolve(false);
        });
    
        return defer.promise;
    }
    

    然后这样称呼它:

    var is_online = false;
    isAppOnline().then(function(data){
        is_online = data;
    });
    

    或者,直接传递 is_connected 对象,比你做的错误处理更好:

    function isAppOnline() {
        var defer = $q.defer();
    
        var is_connected = connectivityMonitor.isInternetConnected();
        is_connected.then(function(result) {
            console.log('INTERNET_CHECK_API : app online');//works fine
            defer.resolve(is_connected);
        }, function(error) {
            console.log('INTERNET_CHECK_API : app offline');//works fine
            defer.reject();
        });
    
        return defer.promise;
    }
    

    【讨论】:

    • 请参考我的回答或 ste2425 关于返回承诺而不是创建新的延迟。
    • 这样它就可以为更复杂的返回对象做好准备了,以防你想做这样的事情:var result = { isConnected: is_connected, ...}。以我的经验,最好这样做,因为随着应用程序的增长,你经常需要在某个时候这样做,所以尽早更好。
    • 不。你真正需要创建一个 deferred 的情况是函数的核心没有返回一个 promise,比如 $timeout。实际上,在可以返回承诺时创建延迟被定义为 promise-antipatterns 之一。
    • 那么你如何解决添加到现有的承诺结果(只是好奇)?
    • function f1 = function (p1) { return f2.then(function(res2) { return p1 + res2; }) } 然后在调用者中:f1('p1').then(function(res1) { // res1 == 'p1' + res2 }) 这只是显示解析路由,拒绝路由几乎与使用 $q.reject 相同。在将 res2 作为 res1 返回之前,您几乎可以对 res2 做任何事情。
    猜你喜欢
    • 2018-01-21
    • 2015-09-27
    • 2021-12-20
    • 2023-03-26
    • 2016-08-23
    • 2013-06-22
    • 1970-01-01
    • 1970-01-01
    • 2015-12-25
    相关资源
    最近更新 更多