简体   繁体   English

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

[英]Swallowed message : Error: Uncaught (in promise): [object Undefined]

My login component briefly displays before being removed by an error message about an undefined object in a promise.我的登录组件在被有关承诺中未定义对象的错误消息删除之前短暂显示。

Here is the promise definition:这是承诺的定义:

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

It is called by:它被称为:

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

The console shows both messages:控制台显示两条消息:

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

I'm using Angular 6.0.4 and tried following this blog我正在使用 Angular 6.0.4并尝试关注此博客

Any way to work around this error so as to keep my login form displayed ?有什么方法可以解决此错误以保持显示我的登录表单?

UPDATE: I tried using an observable instead of a promise but the issue remained the same:更新:我尝试使用 observable 而不是承诺,但问题仍然存在:

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

The whole source code is available on GitHub整个源代码可在GitHub找到

UPDATE: Following an answer below, I also tried to use a then() and a catch() keywords but the error remained the exact same:更新:按照下面的答案,我还尝试使用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');
        });

This is a wild guess, but maybe it's a conflict with Angular's zones.这是一个疯狂的猜测,但也许它与 Angular 的区域冲突。 Since this is a security library it might not like that Angular has replaced core functions with proxies.由于这是一个安全库,因此 Angular 可能不喜欢用代理替换核心功能。 For example, NgZone modifies window.setTimeout and the HTTP methods.例如, NgZone修改window.setTimeout和 HTTP 方法。

So you could try running this code outside of zones.因此,您可以尝试在区域之外运行此代码。 The only problem here is that you're using a static function, and will have to make this an injectable service so that you can access NgZone这里唯一的问题是您使用的是静态函数,并且必须使其成为可注入的服务,以便您可以访问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');
                    });
            });
        }
    }
}

The change here is to use zone.runOutsideAngular这里的变化是使用zone.runOutsideAngular

If you remove the success block, where do you run your logic within success ?如果删除success块,你在哪里运行中的逻辑success

I read some of their source code, I think this is why success causes the problem:我阅读了他们的一些源代码,我认为这就是success导致问题的原因:

Within keycloak.js , there is a function createNativePromise() :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;
}

And it's used this way(simplified code):它是这样使用的(简化代码):

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

    return promise.promise;
}

The problem is, promise.setError() calls promise.reject(result) , so the promise is rejected, it's expecting a catch .问题是, promise.setError()调用promise.reject(result) ,所以 promise 被拒绝,它期待一个catch

But within promise.success , there is a promise.promise.then(callback);但是在promise.success ,有一个promise.promise.then(callback); , and nobody is catching this promise. ,而且没有人能兑现这个承诺。

This is why you get the Uncaught (in promise): [object Undefined] , and in my case, i always get Uncaught (in promise): true .这就是为什么你得到Uncaught (in promise): [object Undefined] ,在我的例子中,我总是得到Uncaught (in promise): true

Solution:解决方案:

Notice that promise.promise is a real Promise , so we can use then and catch instead of success and error .注意promise.promise是一个真正的Promise ,所以我们可以使用thencatch而不是successerror

The drawback is, the typescript type will be wrong.缺点是打字稿类型会出错。

We have observed a similar error about the promise object undefined.我们观察到了一个关于 promise 对象 undefined 的类似错误。 The situation was our local application was working fine with the local keycloak standalone server but faced this error when the local application trying to connect with a keycloak server hosted on the ABC server (ABC is used as a reference here to give any arbitrary name).情况是我们的本地应用程序在本地 keycloak 独立服务器上运行良好,但是当本地应用程序尝试连接托管在 ABC 服务器上的 keycloak 服务器时遇到此错误(此处使用 ABC 作为参考以提供任意名称)。

This issue was resolved when we hosted the application and the keycloak server both on the ABC server.当我们在 ABC 服务器上托管应用程序和 keycloak 服务器时,这个问题得到了解决。

It looks like there are some time sync issues in different machines due to which the promise object is not returned.看起来在不同的机器上存在一些时间同步问题,因为没有返回承诺对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM