4

tengo el siguiente problema: Cuento con un Objeto que consta de length 0 pero si imprimo el objeto si me devuelve resultados. Os dejo una imagen de la consola:

Como podéis ver indica que tiene length == 73 pero a su vez en proto indica que es un Array(0)

Como podéis ver indica que tiene length == 73 pero si imprimo por consola objeto.length me devuelve 0!

El problema es que cuando ejecuto un console.log( objeto.length ) me devuelve 0. Esto me impide que pueda iterar sobre los valores para operar con ellos.

Os dejo además las 3 funciones que se ven afectadas:


async getToken(){
   const response = this.http.post('[url]', this.tokenHeaders );
   response.subscribe(data => {
     this.token = 'Bearer ' + data['access_token'];
   });
   await this.getAllApiIDs().then( this.getAllApis() );
 }

En la primera función únicamente llamo a las otras dos funciones.

  async getAllApiIDs(){
    const respuesta : string[] = [];
    await this.getAPIs('apis').subscribe( async ( apis: Api[] ) => {
      for ( let api of apis ){
        respuesta.push( api['id'] );
      }
    });
    this.all_api_id = respuesta;
  }

En esta segunda función añado los valores a la variable en cuestión (this.all_api_id), la cual si realizo un console.log me devuelve un resultado, con length 73 pero el proto indica que es un Array(0), como se ve en la imagen

 getAllApis(){

    const apis: Api[] = [];

      for ( var id of this.all_api_id ){

        console.log( id );
      }
 }

Por lo tanto en esta tercera función donde intento iterar sobre los valores del objeto (this.all_api_id) al contar con length == 0 no entra en el bucle for

Si hago un console.log(this.all_api_id.length) siempre me devuelve 0 pero si hago un console.log(this.all_api_id) me imprime un resultado en consola como se ve en la imagen.

La variable this.all_api_id es un objeto de tipo string[]

¿Alguien sabe por qué se comporta de esta manera?

¿Sabéis alguna solución para que pueda iterar sobre this.all_api_id?

MANZARBEITIA
  • 512
  • 1
  • 12
  • 2
    ***¿Alguien sabe por qué se comporta de esta manera?*** Se comporta así porque es un proceso asíncrono, por lo tanto, a menos que tu entorno global sea totalmente asíncrono, no podrás establecer variables globales como resultado de procesos asíncronos. Todas las variables creadas dentro de una función asíncrona, son accesibles dentro de su entorno, y sólo pueden ser accedidas (desde fuera, aunque no es realmente así) bien usando `then()` o usando funciones `callback`, o como ya dije al principio, las puedes devolver a otro entorno asíncrono superior usando `await`. – Mauricio Contreras Sep 18 '20 at 11:50
  • 1
    Tal vez esto te sea de ayuda https://es.stackoverflow.com/a/388941/101499 – Nicolas Oñate Sep 18 '20 at 12:01
  • Hola @MauricioContreras, añadí el .then() como indicas sin resultados =( Sigo teniendo una duda acorde a tu comentario. ¿A qué se debe que una variable global no pueda igualarse a un proceso asíncrono? En mi caso es un proceso asíncrono finito que igualo primero a una variable local ¿Tampoco puede ser posible de realizar? Muchas gracias! – MANZARBEITIA Sep 23 '20 at 06:36

1 Answers1

3

Creo que estás confundiendo cosas: Primero veamos qué podemos investigar de esa propiedad __proto__:

const array = 'Texto'.split('');
//tenemos un array con 5 elementos (letras)
console.log(array.toString());

console.log('Su longitud es',array.length);
//Como todo objeto, tiene una propiedad __proto__
console.log(array.__proto__)
console.log('objeto vacío', {}.__proto__);

//Veamos qué tiene __proto__
class MiClase {
  constructor() {}
  metodo() {return 'hola'};
}
const o= new MiClase();
console.log('MiClase', o.__proto__);
console.log('MiClase', o.__proto__.metodo);

Resulta que este atributo existe en todos los objetos que crees en Javascript, y no es más que una especie de acceso directo a los métodos de clase.

Es decir, es como si el intérprete, al crear un objeto de cierta clase, digamos que Array hiciera algo así como:

instancia.__proto__ = Array.prototype;

Acceder a esta propiedad no es recomendable, como podemos leer en la documentación oficial de Mozilla:

La propiedad __proto__ de Object.prototype es una propiedad llamada de acceso (una función getter y también función setter) que provee acceso al interior de [[Prototype]] (ya sea un objeto o null) del objeto a través del cual se accede a ella.

El uso de la propiedad __proto__ es polémico actualmente, y está rechazado. Originalmente, nunca fué incluído en la especificación de EcmaScript, pero los navegadores modernos decidieron implementarla de todas maneras. Sólo actualmente, la propiedad __proto__ ha sido estandarizada en la especificación del lenguaje ECMAScript 6, para asegurar la compatibilidad entre navegadores, por lo tanto, esta será soportada en el futuro. Actualmente está obsoleta en favor de Object.getPrototypeOf/Reflect.getPrototypeOf y Object.setPrototypeOf/Reflect.setPrototypeOf (aunque todavía establecer el [[Prototype]] de un objeto es una operación muy lenta, por lo que si nos preocupa el rendimiento, debemos de evitarlo).

Así que array.length te devuelve el número de elementos de la instancia guardada en la variable array, mientras que array.__proto__.length devuelve 0 porque es el valor por defecto al crear un array vacío.

Por otro lado, tienes una extraña mezcla de Promesas y Observables...

async getAllApiIDs(){
    const respuesta : string[] = [];
    await this.getAPIs('apis').subscribe( async ( apis: Api[] ) => {
      for await ( let api of apis ){
        respuesta.push( api['id'] );
      }
    });
    this.all_api_id = await respuesta;
  }

A menos que la llamada a this.getAPIs('apis') genere un stream de promesas, usar async en el for no sirve de nada ahí. Además, el método subscribe devuelve un Subscription, no una promesa. De nuevo, estás usando await donde no toca.

Pablo Lozano
  • 45,934
  • 7
  • 48
  • 87
  • Muchas gracias por tu respuesta Pablo, ya entiendo como funciona el "proto", viene siendo algo parecido a las clases en java. He editado mi código (intentando no tener esa extraña mezcla entre Promesas y Observables) pero sigo sin poder iterar el bucle for de getAllApis(). He probado añadiendo console.log(Object.keys(this.all_api_id).length); pero me sigue devolviendo 0. Alguna idea de como iterar el for¿? – MANZARBEITIA Sep 23 '20 at 06:52
  • Prueba a meter la asignación `this.all_api_id = respuesta;` dentro de la función que le pasas a `subscribe`. – Pablo Lozano Sep 23 '20 at 07:40
  • Nada, sigue sin entrar en el bucle for =S Estoy convencido que tiene que ver con el length=0, como es 0 nunca llega a iterar el bucle. He probado añadiento el for clasico de js: for ( var i=0; i< 73; i++) [Pongo 73 "a las bravas" pq se cual es el numero pero no deberia ser asi] y hago un console.log( this.all_api_id[i] ) y en este caso me devuelve las 73 veces "undefined" mientras que si hago console.log( this.all_api_id) me devuelve los 73 resultados en un Array – MANZARBEITIA Sep 23 '20 at 07:50
  • 1
    ¿No será que `this.all_api_id` no es un array, sino que tiene un array dentro? – Pablo Lozano Sep 23 '20 at 10:30
  • Coincido con @Pablo_Lozano – sgClaudia98 Sep 23 '20 at 22:26