【问题标题】:Return value instead of a promise [to stop nested deferred promise]返回值而不是承诺 [停止嵌套的延迟承诺]
【发布时间】:2017-11-24 09:42:27
【问题描述】:

我有一堆嵌套的函数,因为顶层函数是一个 ajax 请求。 所以我想在嵌套子函数中返回一个值而不是一个承诺。

家长

let getUserPermissions = function(id) {
      let deferred = $q.defer();
      let promise = accessRequestService.getPermissions(id);
      promise.then(function(data) {
        deferred.resolve(data);
      }, function(err) {
        deferred.reject(err);
      })
      return deferred.promise;
    }

孩子 1

$rootScope.userInit = function() {
        return getUserPermissions(vzid)
          .then(function(data) {

            //Some code here

            return data;
          })

    }

孩子 2

let checkAuthorize = function(toState) {
  return $rootScope.userInit().then(
    function(data) {
//some code here 
      return data;
    });
}

3 级

checkAuthorize(toState).then( function(val){ 
 $rootScope.isAuthorized = val;
  if ($rootScope.isAuthorized == true) {
        $log.info('is Authorized')
      } else {
        $log.info('is not Authorized');
        throw new AuthorizationError()
      }
  })

在第 3 级,我们仍在使用 Promise。 child 2 可以返回一个值而不是承诺。

3 级期望

$rootScope.isAuthorized = checkAuthorize(toState);

  if ($rootScope.isAuthorized == true) {
      $log.info('is Authorized')
      } else {
      $log.info('is not Authorized');
      throw new AuthorizationError()
     }

【问题讨论】:

  • getUserPermissions 不等同于简单的function(id) { return accessRequestService.getPermissions(id); } 吗?无需纠结Deferred
  • @Thomas 用于基于服务发送的成功/错误的另一个计算。此代码中不存在。
  • 那么你仍然可以做function(id) { return accessRequestsService.getPermissions(id).then(function(data) { ... }, function(err) { ... }); }。您很少需要直接使用defer(),除非您正在编写自己的承诺“源”。
  • 不能返回一个值而不是一个promise,这就是异步执行的重点,你处理latter的值回调或承诺
  • async-await 是您需要的,但取决于您所针对的 ES 版本

标签: javascript angularjs es6-promise deferred


【解决方案1】:

铁的事实是:你不能,除非你想到处都是意大利面条代码。

最好的解决方案是使用ui-router's resolve 之类的东西,在页面显示给用户之前获得所需的所有权限。然后,您可以在控制器上使用它们而无需任何异步调用。

【讨论】:

  • 我认为你甚至不能到处使用意大利面条代码
  • 不要低估意大利面条代码的力量哈哈@lcycool
  • @tiagodws 很好的建议,但我们仍在使用非常不同的 UI 路由方法。非常感谢。
  • 我正在使用 $state.transitionTo 方法在 stateChangeStart 之前调用。 $state.transitionTo = function(to, toParams, options) { return checkAuthorize(to).then(function(auth) { $rootScope.isAuthorized = auth; return transitionTo(to, toParams, options) }) }
【解决方案2】:

您可以使用它async/await 构造。并使用 Babel 来支持旧浏览器。

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

async function f1() {
  var x = await resolveAfter2Seconds(10);
  console.log(x); // 10
  console.log('done');
}

f1();

【讨论】:

  • Native Promise(以及因此 async/await)不是应该为 AngularJS 应用程序建议的解决方案,除非需要,因为它没有考虑 Angular 摘要周期。 async/await 不会在 Angular 应用程序中按预期工作,而 $q 承诺会。
  • @estus $q 是受 Kris Kowal 的 Q 启发的 Promises/A+ 兼容的 promise/deferred 对象的实现。对 Q Promise 的 Async/Await 语法支持 — github.com/audreyt/q-jscex
  • 我知道这一点。我假设您不是 AngularJS 用户。 $q 实现与其他 Promise/A+ 实现有根本区别。 $q 与摘要循环紧密相关,并且可以是同步的,而其他实现则不是。如果 Angular 用户在没有额外措施的情况下向应用程序抛出 async/await,这可能会导致无法预料的结果。当前状态下的答案对 AngularJS 用户没有好处,因为它没有解决特定于框架的问题。
【解决方案3】:

是的,这种类型的事情是可能的,但它会改变行为。您可能希望保留userInit,但您还添加了一个userInitValue 变量并将其初始化如下:

let userInitValue = null;
let userInit = function() {
    return getUserPermissions()
      .then(function(data) {
        userInitValue = data;
        return data;
      })
}

所以现在userInitValue 将以 null 开始,然后再初始化为相关数据。

function isKnownAuthorized(toDoSomething) {
    // If we don't know whether the user is authorized
    //   because we are still waiting for the server to tell us
    //   then return false and disallow access for now
    if(!userInitValue) return false;

    // Otherwise return the truth
    //   (as of when we got the server response)
    return userInitValue.isAuthorized(toDoSomething);
}

再次注意行为的变化。获得即时响应的代价可能是在服务器为您提供数据之前,即时响应可能是错误的。所以不要在 AngularJs 的一次性 :: 表达式中使用它。

【讨论】:

    【解决方案4】:

    根据您希望在第 3 级中实现的目标,我猜想这个函数将使用相同的输入多次调用。在这种情况下,如果没有缓存结果,我会调用 promise 并缓存结果。这样你就不必沿着承诺链走下去,尽管我在提供的代码中只计算了一个承诺。有多个处理程序正在解析,但只有一个承诺。

    【讨论】:

      【解决方案5】:

      您可以使用nsynjs 像同步运行代码一样运行您的代码:它将逐步评估代码,如果某些函数返回承诺,它将暂停执行,等待承诺解决,并分配解决结果到data 财产。因此,下面的代码将在级别 1 暂停,直到 promise 被解析为实际值。

      var getUserPermissions = function(id) {
          return new Promise(function(resolve, reject) {
              setTimeout(function(){
              resolve({
                      id: id,
                      isAdmin: "yes he is",
                  })
              }, 1000);
          });
      };
      
      function synchronousCode() {
          console.log("start");
          var vzid = 35;
          var userInit = function() {
              return getUserPermissions(vzid).data;
          };
          var checkAuthorize = function() {
      	    return userInit().isAdmin;
          };
          var isAuthorized = checkAuthorize();
          console.log(isAuthorized);
      };
      	
      nsynjs.run(synchronousCode, null, function(){
      	console.log("finish");
      });
      <script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>

      【讨论】:

        【解决方案6】:

        我正在使用 $state.transitionTo 方法在$stateChangeStart 之前调用。

        var transitionTo = $state.transitionTo;
        $state.transitionTo = function(to, toParams, options) {
          var from = $state.$current,
            fromParams = $state.params;
        
          to = to.name ? to : $state.get(to);
        
            $rootScope.state = {
              to: to.self,
              toParams: toParams,
              from: from.self,
              fromParams: fromParams,
              options: options
            }
        
          if (options.notify && options.notify !== false) {
            return $q.reject(new AuthorizationError('Rejecting $state.transitionTo', 'Transition Rejected'));
          } else {
            return checkAuthorize(to).then(function(auth) {
              $rootScope.isAuthorized = auth;
                return transitionTo(to, toParams, options)
            })
          }
        }
        

        StateChangeStart

        $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
          $log.info("Route change start from", fromState.url, "to", toState.url);
          //event.preventDefault();
        
          if ($rootScope.isAuthorized == true) {
            $log.info('is Authorized')
            //$state.go($rootScope.toState.name);
          } else {
            event.preventDefault();
            $log.info('is not Authorized');
            throw new AuthorizationError('User is not Authorized.', 'NOT_AUTHENTICATED')
          }
        
        });
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-03-15
          • 1970-01-01
          • 1970-01-01
          • 2015-03-08
          • 1970-01-01
          • 1970-01-01
          • 2017-04-26
          相关资源
          最近更新 更多