将多个嵌套的 Promise 和 Promise 数组转换为 Observables

Posted

技术标签:

【中文标题】将多个嵌套的 Promise 和 Promise 数组转换为 Observables【英文标题】:Converting multiple nested promises and array of promises to Observables 【发布时间】:2019-05-19 23:38:33 【问题描述】:

我正在使用 TypeScript 将 AngularJS 应用程序转换为 Angular 7。

我在将一些复杂的嵌套 Promise 转换为 Observables 时遇到了一些问题。

这是我正在处理的代码示例:

signup.component.js

function SomethingSignupController(somethingApplication) 
    function activate() 
        getApplication();
     

    function getApplication() 
        vm.getFromServer = false;
        vm.promises = [];
        SomethingRepo.get().then(function(application) 
            vm.getFromServer = true;
            vm.application = application;
            vm.promises.push(Something.getCompany().then(function(company) 
                vm.company = company;
                if (vm.company.structure === ‘more_25’) 
                    return SomethingRepo.getAllOwners().then(function(owners) 
                        vm.owners = owners;
                        for(var i = 0; i < vm.owners.length; i++) 
                            vm.promises.push(getOwnerFiles(vm.owners[i]));
                        
                    
                
            
            vm.promises.push(SomethingRepo.getSomethingOne().then(function(somethingOne) 
                vm.somethingOne = somethingOne;
            
            vm.promises.push(SomethingRepo.getSomethingTwo().then(function(somethingTwo) 
                vm.somethingTwo = somethingTwo;
            
            vm.promises.push(SomethingRepo.getSomethingThree().then(function(somethingThree) 
                vm.somethingThree = somethingThree;
            
            /* and a few more like the above */
            $q.all(vm.promises).then(function()
                postGet();
            ).finally(function() 
                vm.promises = [];
            );
        
    

    function postGet() 
        /* does something with the data acquired from SomethingRepo */
    

    /* when an application is send */
    function send() 
        somethingApplication.promises = [];
        somethingApplication.errors = [];
        if (vm.getFromServer) 
            update();
         else  
            create();
        
    

    function update() 
        somethingApplication.promises.push(SomethingRepo.update(vm.application).then(angular.noop, function(error) 
            somethingApplication.parseErrors(error, ‘Some error’);
        ));
        patchInfo();
    

    function create() 

    

    function patchInfo() 
        somethingApplication.promises.push(SomethingRepo.patchAccount(vm.account).then(angular.noop, function(error) 
            somethingApplication.parseErrors(error, ‘Account error: ‘);
        
        /* a few more patches */
        $q.all(somethingApplication.promises).then(function() 
            /* display dialog */
        , angular.noop).finally(function() 
            postGet();
            somethingApplication.promises = [];
            if (somethingApplication.errors.length >= 1) 
                vm.errors = somethingApplication.errors;
            
        );
    

somethingApplication.service.js

function somethingApplication(SomethingRepo) 
    var promises = [], errors = [];

    var service = 
        promises: promises;
        errors = errors;
        parseErrors: parseErrors;
    ;

    return service; 


    function parseErrors(error, base_string) 
        angular.forEach(error.data.erros, function(value_params, key_params) 
            this.errors.push(base_string + ‘ ‘ + key_params.replace(/_/g, ‘ ‘) + ‘ ‘ + value_params);
        , this);
    

somethingRepo.js

function SomethingRepo(Server) 
    function get() 
        return Server.get(‘/something/application’, null, noGlobal: true);
    

我已经减少了文件,但它们包含更多这样的代码。 控制器的目的是为另一个网站创建或更新应用程序。在我的网站上,我有一个与另一个网站上的表单相对应的字段表单。如果您已经提交了申请,但想要更新它,您已经提交的信息将从其他网站加载。

问题是,为了创建或更新应用程序,许多不同的端点请求发布到。

在 AngularJS 中,我存储来自每个请求的 Promise,并在最后异步运行它们。在 TypeScript 和 Angular 中,我想使用 Observables 并订阅数据更改。

我该如何开始?如何从另一个 Observable 订阅 Observable 所需的参数?任何建议如何进行?

【问题讨论】:

Observable 支持 ToPromise 方法,将 Observable 转换为可调用的 Promise。如果您想保留Promise.all 方法,您可以。否则,您可以考虑使用 RxJs 建议的其他方法,例如 .concatMapforkJoin 等。尽可能利用async await,这将帮助您跟踪它在迁移阶段所做的工作。 “我如何开始?如何订阅另一个 Observable 的所需参数?任何建议如何继续?”如果我理解正确,您正在寻找forkJoin。您可以将其与 from(promise) 结合使用以创建一个可观察的对象,该可观察对象等待一组承诺解决,然后发出已解决值的列表。 我不想保留Promise 方法。你有一个具体的例子来说明我如何forkJoin 我的特定代码吗?不是我想让你做这项工作,但我很难将我读到的关于 forkJoin 的内容应用到我的代码中 这可能会有所帮助 - ***.com/questions/39319279/… @FrankfromDenmark forkJoin 与 Promise.all 完全一样:您提供 Observables 的数组(或列表)(angular 的 HttpClient.get 返回一个Observable 默认情况下),它的回调会触发所有解析的值。 FK82 提供了上面的官方文档。看看示例 5,它非常接近您的用例。 【参考方案1】:

这是一个示例,展示了如何在场景中轻松使用可观察对象 -

你的服务应该是这样的 -

import  Injectable  from '@angular/core';
import  AppConstants  from '../../app.constants';
import  HttpClient  from '@angular/common/http';

@Injectable()
export class ExampleService 
    constructor(private appconstants: AppConstants, private http: HttpClient)  

    get() 
        return this.http.get(this.appconstants.apiUrl);
    

    getSomethingOne() 
        return this.http.get(this.appconstants.apiUrl1);
    

    getSomethingTwo() 
        return this.http.get(this.appconstants.apiUrl2);
    

然后只需在您的组件中使用它,如下所示 -

import  Component  from '@angular/core';
import  forkJoin  from 'rxjs';
import  ExampleService  from '../services/example.service';

@Component(
  selector: 'app-example',
  templateUrl: './example.component.html',
  styleUrls: ['./example.component.css']
)
export class ExampleComponent 

    data;
    dataOne;
    dataTwo;

    constructor(private exampleService: ExampleService)  

    getApplication() 
        const combined = forkJoin([
            this.exampleService.get(),
            this.exampleService.getSomethingOne(),
            this.exampleService.getSomethingTwo()
        ]);
        combined.subscribe(res => 
            this.data = res[0];
            this.dataOne = res[1];
            this.dataTwo = res[2];
        );
    

【讨论】:

两个问题:1) 我不能让我的服务返回一个 Observable,例如:get(): Observable&lt;Application&gt; return this.client.get&lt;Application&gt;(this.appconstants.apiUrl).pipe( map((item: any) =&gt; return Object.assign(new Application(), item); ) ); 2) 我将如何使用来自 forkJoin 的结果来创建另一个请求?我可以链接它,还是应该在之后提出单独的请求? 1) 这种方法也很好,也是一种很好的做法。 2)您可以从您的函数中返回一个Observable - getApplication(),然后在其订阅中执行进一步的操作 这是一个参考。用于创建 observable - medium.com/@luukgruijs/… 非常感谢

以上是关于将多个嵌套的 Promise 和 Promise 数组转换为 Observables的主要内容,如果未能解决你的问题,请参考以下文章

将嵌套循环查询组合到父数组结果 - pg-promise

用Promise解决多个异步Ajax请求导致的代码嵌套问题

Promise

浅谈promise对象

嵌套 Promise 不会将错误传播到 Node.js 中的父 Promise?

Promise和模块块化编程