网站搜索

javascript - 在 angularJS 和 Typescript 中使用 Promise


我有以下函数,我的目标是向 items 列表中推送,当组件可以识别它们的父项时。我的问题是,当我推送到列表时,console.log() 显示对象确实在里面,但当我返回列表并在另一个函数中捕获时,列表中什么都没有。我认为 items 列表在上面的代码执行完成之前就被返回了。

 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 [];
     });
 }

答案#1

恐怕问题出在你的方法上,而不是代码本身。Promise 就是一个承诺——承诺将来某个时间提供数据。所以,当你的函数返回(立即返回)时,Promise 还没有被解决,你的调用函数获取到的数据仍然是空的。我在你的代码中没看到 console.log(),但我怀疑你把它放在了 then() 内部,而 then() 会在数据接收到之后才被调用。所以你会看到数据被记录下来。问题是,到那时 get_items_for_request() 已经返回,你的调用函数也已经继续执行了。

如果你想使用 Promise,你必须使用回调函数,在 Promise 被解决后调用。但是如果你想要实际数据返回给调用者,你需要同步获取数据。

对于同步抓取,请检查此响应。但请注意,同步抓取会影响脚本的响应性。

对于异步获取(使用 promises),你需要定义一个回调函数,该函数在所有数据获取完成后调用。我不会尝试修复你的代码,但它的结构应该大致如下所示(用 JavaScript 表示)。不过请记住,这只是一个草图。

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

答案#2

在第一个 promise 回调中,你有两个 return,只有第一个会执行。在第一个 for 之后你返回了一个 promise,而你将其解析为 undefined。你应该等待一个 promise 数组,每个 promise 对应一个 this.ItemComponent.forItem 调用。

Promise.all 可以帮助做到这一点: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

你应该这样做:

    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 [];
    } )

如果你只想要一个项目,请在结果数组中找到第一个非假值元素