吞下消息:错误:未捕获(在承诺中):[对象未定义]

时间:2018-04-24 15:27:34

标签: javascript angular promise angular-promise keycloak

我的登录组件会在被有关promise中未定义对象的错误消息删除之前暂时显示。

这是承诺定义:

  static init(): Promise<any> {
    KeycloakClientService.auth.loggedIn = false;
    return new Promise((resolve, reject) => {
      const keycloakConfig = {
      url: environment.KEYCLOAK_URL,
      realm: environment.KEYCLOAK_REALM,
      clientId: environment.KEYCLOAK_CLIENTID,
      'ssl-required': 'external',
      'public-client': true
      };
      const keycloakAuth: any = new Keycloak(keycloakConfig);

      keycloakAuth.init({onLoad: 'check-sso'})
        .success(() => {
          KeycloakClientService.auth.loggedIn = true;
          KeycloakClientService.auth.authz = keycloakAuth;
          KeycloakClientService.auth.logoutUrl = environment.KEYCLOAK_URL
          + '/realms/' + environment.KEYCLOAK_REALM + '/protocol/openid-connect/logout?redirect_uri='
          + document.baseURI;
          console.log('=======>> The keycloak client has been initiated successfully');
          resolve('Succeeded in initiating the keycloak client');
        })
        .error(() => {
          reject('Failed to initiate the keycloak client');
        });
    });
  }

它被称为:

KeycloakClientService.init()
  .then(
    () => {
      console.log('The keycloak client has been initialized');
    }
  )
  .catch(
    (error) => {
      console.log(error);
      window.location.reload();
    }
  );

控制台显示两条消息:

The keycloak client has been initiated successfully
The keycloak client has been initialized

我正在使用Angular 6.0.4并尝试遵循此blog

是否有办法解决此错误,以便显示我的登录表单?

更新:我尝试使用observable而不是promise,但问题仍然存在:

  public init(): Observable<any> {
    KeycloakClientService.auth.loggedIn = false;
    return new Observable((observer) => {
      const keycloakConfig = {
        'url': environment.KEYCLOAK_URL,
        'realm': environment.KEYCLOAK_REALM,
        'clientId': environment.KEYCLOAK_CLIENTID,
        'ssl-required': 'external',
        'public-client': true
      };
      const keycloakAuth: any = new Keycloak(keycloakConfig);

      keycloakAuth.init({ 'onLoad': 'check-sso' })
        .success(() => {
          KeycloakClientService.auth.loggedIn = true;
          KeycloakClientService.auth.authz = keycloakAuth;
          KeycloakClientService.auth.logoutUrl = environment.KEYCLOAK_URL
            + '/realms/' + environment.KEYCLOAK_REALM + '/protocol/openid-connect/logout?redirect_uri='
            + document.baseURI;
          console.log('The keycloak auth has been initialized');
          observer.next('Succeeded in initiating the keycloak client');
          observer.complete();
        })
        .error(() => {
          console.log('The keycloak client could not be initiated');
          observer.error('Failed to initiate the keycloak client');
        });
    });
  }

整个源代码可在GitHub

上找到

更新:根据以下答案,我还尝试使用then()catch()个关键字,但错误仍然完全相同:

keycloakAuth.init({ 'onLoad': 'check-sso' })
        .then(() => {
          KeycloakClientService.auth.loggedIn = true;
          KeycloakClientService.auth.authz = keycloakAuth;
          KeycloakClientService.auth.logoutUrl = environment.KEYCLOAK_URL
            + '/realms/' + environment.KEYCLOAK_REALM + '/protocol/openid-connect/logout?redirect_uri='
            + document.baseURI;
          console.log('The keycloak auth has been initialized');
          observer.next('Succeeded in initiating the keycloak client');
          observer.complete();
        })
        .catch(() => {
          console.log('The keycloak client could not be initiated');
          observer.error('Failed to initiate the keycloak client');
        });

3 个答案:

答案 0 :(得分:3)

这是一个疯狂的猜测,但也许它与Angular的区域发生冲突。由于这是一个安全库,它可能不喜欢Angular用代理替换了核心功能。例如,NgZone修改了window.setTimeout和HTTP方法。

因此,您可以尝试在区域外运行此代码。这里唯一的问题是您使用静态功能,并且必须使其成为可注射服务,以便您可以访问NgZone

@Injectable()
export class KeycloakClientService {
    public constructor(private zone: NgZone) {
    }

    public init(): Promise<any> {
        KeycloakClientService.auth.loggedIn = false;
        return new Promise((resolve, reject) => {
            this.zone.runOutsideAngular(() => {
                const keycloakConfig = {
                     url: environment.KEYCLOAK_URL,
                     realm: environment.KEYCLOAK_REALM,
                     clientId: environment.KEYCLOAK_CLIENTID,
                     'ssl-required': 'external',
                     'public-client': true
                };

                const keycloakAuth: any = new Keycloak(keycloakConfig);

                keycloakAuth.init({onLoad: 'check-sso'})
                    .success(() => {
                        KeycloakClientService.auth.loggedIn = true;
                        KeycloakClientService.auth.authz = keycloakAuth;
                        KeycloakClientService.auth.logoutUrl = environment.KEYCLOAK_URL
                            + '/realms/' + environment.KEYCLOAK_REALM + '/protocol/openid-connect/logout?redirect_uri='
                            + document.baseURI;
                        console.log('=======>> The keycloak client has been initiated successfully');
                        resolve('Succeeded in initiating the keycloak client');
                    })
                    .error(() => {
                        reject('Failed to initiate the keycloak client');
                    });
            });
        }
    }
}

此处的更改是使用zone.runOutsideAngular

答案 1 :(得分:0)

如果您删除了success块,那么您将在success内的何处运行逻辑?

我阅读了他们的一些源代码,我认为这就是success引起问题的原因:

keycloak.js中,有一个函数createNativePromise()

function createNativePromise() {
var p = {
    setSuccess: function(result) {
        p.success = true;
        p.resolve(result);
    },

    setError: function(result) {
        p.success = false;
        p.reject(result);
    }
};
p.promise = new Promise(function(resolve, reject) {
    p.resolve = resolve;
    p.reject = reject;
});
p.promise.success = function(callback) {
    p.promise.then(callback);
    return p.promise;
}
p.promise.error = function(callback) {
    p.promise.catch(callback);
    return p.promise;
}
return p;
}

以这种方式使用(简化代码):

function refreshToken() {
    var promise = createNativePromise();
    ...
    if (refreshTokenFailed) {
        promise.setError(true);
    }
    ...

    return promise.promise;
}

问题是,promise.setError()调用promise.reject(result),因此承诺被拒绝,它期望catch

但是在promise.success中,有一个promise.promise.then(callback);,没有人能兑现这一诺言。

这就是为什么您获得Uncaught (in promise): [object Undefined]的原因,在我来说,我总是得到Uncaught (in promise): true

解决方案:

请注意,promise.promise是真实的Promise,因此我们可以使用thencatch代替successerror

缺点是打字稿类型会错误。

答案 2 :(得分:0)

我们观察到了一个关于 promise 对象 undefined 的类似错误。情况是我们的本地应用程序在本地 keycloak 独立服务器上运行良好,但是当本地应用程序尝试连接托管在 ABC 服务器上的 keycloak 服务器时遇到此错误(此处使用 ABC 作为参考以提供任意名称)。

当我们在 ABC 服务器上托管应用程序和 keycloak 服务器时,这个问题得到了解决。

看起来在不同的机器上有一些时间同步问题,因为没有返回 promise 对象。