javascript - Promesas con angularJS y Typescript
Tengo la siguiente función, mi objetivo es agregar a la lista de elementos, cuando los componentes puedan identificar su elemento padre. El problema que tengo es que cuando estoy agregando a la lista, console.log() me muestra que el objeto está allí, pero cuando devuelvo la lista y la obtengo en otra función, no hay nada en la lista. Creo que la lista de elementos se devuelve antes de que se complete el código anterior.
private get_items_for_request() {
return this.Item.forRequest(this.request.id, ['group'])
.then((_items) => {
var items = [];
for (var item of _items) {
return this.ItemComponent.forItem(item.id, ['type'])
.then((_components) => {
for (var component of _components) {
if (component.type.can_identify_item) {
items.push({
group_id: item.group.reference,
identifier_code: this.remove_check_digit_if_necessary(
component.identifier_code),
quantity: 1
});
break;
}
}
}, (reason) => {
this.Toast.error(
this.gettextCatalog.getString('components.load_failed'));
return [];
});
}
return items;
}, (reason) => {
this.Toast.error(
this.gettextCatalog.getString('items.failed_load'));
return [];
});
}
Respuesta #1
Me temo que el problema está en tu enfoque, no en el código en sí. Una promesa es justamente eso: una promesa de datos en algún momento futuro. Así que, cuando tu función retorna (inmediatamente), la promesa aún no se ha resuelto y los datos que captura tu función llamadora todavía están vacíos. No veo console.log() en tu código, pero sospecharía que lo pusiste en algún lugar dentro de then(), que se llamará una vez que se hayan recibido los datos. Así que verás que los datos se registran. El problema es que para ese momento get_items_for_request() ya ha retornado y tu función llamadora ya ha seguido adelante.
Si quieres usar una promesa, tienes que usar un callback para que se llame una vez que la promesa se resuelva. Pero si quieres que los datos reales sean devueltos al llamador, necesitas obtener los datos de manera síncrona.
Para la obtención síncrona, verifica esta respuesta. Pero ten en cuenta que la obtención síncrona afectará la capacidad de respuesta de tu script.
Para una obtención asincrónica (usando promesas), necesitas definir una función de retorno llamada una vez que todos los datos hayan sido obtenidos. No intentaré arreglar tu código, pero debería seguir el siguiente esquema en JavaScript. Aunque ten en cuenta que es solo un esquema.
function get_items_for_request(onSuccess) {
var items = []
var total = -1
var sofar = 0;
this.Item.forRequest(this.request.id, ['group'])
.then(function (_items) {
var items = [];
total = _items.length // remember how many nested calls will be made
for (var item of _items) {
this.ItemComponent.forItem(item.id, ['type'])
.then(function (_components) {
// push received data to the items array here
sofar++
if (sofar == total) { // all done
onSuccess(items)
}
}
}
}
}
Respuesta #2
En el primer callback de la promesa, tienes dos retornos, solo se ejecuta el primero. Devuelves una promesa después del primer for, la cual resuelves como undefined. Deberías esperar a un array de promesas, cada una correspondiente a una llamada a this.ItemComponent.forItem.
Promise.all ayuda con esto: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Deberías hacer algo como esto:
return this.Item.forRequest(this.request.id, ['group']).then((_items) => {
return Promise.all(_items.map(function (item) {
return this.ItemComponent.forItem(item.id, ['type']).then((_components) => {
for (var component of _components) {
if (component.type.can_identify_item) {
return {
group_id: item.group.reference,
identifier_code: this.remove_check_digit_if_necessary(
component.identifier_code),
quantity: 1
};
}
}
}, (reason) => {
this.Toast.error(
this.gettextCatalog.getString('components.load_failed'));
return [];
});
}));
}, (reason) => {
this.Toast.error(
this.gettextCatalog.getString('items.failed_load'));
return [];
} )
Si solo quieres un elemento, encuentra el primer elemento no falso en el arreglo resultante
