如何处理服务迟到的数据? [英] How to handle data comes late from service?

查看:27
本文介绍了如何处理服务迟到的数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的 Angular 应用程序中,我需要将数据存储到一个在初始阶段为空的数组中.

示例:

someFunction() {让数组 = [];console.log("第一步");this.service.getRest(url).subscribe(result => {result.data.forEach(元素 => {console.log("步骤 2");array.push(元素);//推送所有对象来自 res.data});console.log("步骤 3");});console.log("步骤 4");}

这里我列出了带有步骤顺序的 console.log().

调用函数时的顺序是,

步骤 1第 4 步第2步第三步

这里在第 1 步之后,第 4 步调用,然后是第 2 步..所以如果我 console.log(array) 代替第 4 步,它再次给出空数组..>

但是代替第2步和第3步,它提供了价值.从服务中出来,价值是空的.

因此我总是在 array 中得到空值.

请帮助我将数据存储到变量中,即使服务调用和响应返回的持续时间也是如此.

通过修改代码尝试了很长时间但无法使其工作..

编辑:

我在下面给出了我目前正在使用的实时应用程序 stackblitz 链接 https://stackblitz.com/edit/angular-x4a5b6-ng8m4z

在此演示中查看文件 https://stackblitz.com/edit/angular-x4a5b6-ng8m4z?file=src%2Fapp%2Fquestion.service.ts

我在哪里使用服务调用..如果我把 async getQuestions() {},它给出了 questions.forEach of undefined

的错误

service.ts

 jsonData: any = [{"elementType": "文本框","class": "col-12 col-md-4 col-sm-12","key": "project_name","label": "项目名称","类型": "文本",价值": "",必需":假,最小长度":3,最大长度":20,订单":1},{"elementType": "文本框","class": "col-12 col-md-4 col-sm-12","key": "project_desc","label": "项目描述","类型": "文本",价值": "",必需":真的,订单":2},{"elementType": "下拉菜单","key": '项目',"label": '项目评级',选项": [],订单":3}];获取问题(){让问题:任何= [];//在上面的选项"中有空值的JSON中:[],this.jsonData.forEach(element => {if (element.elementType === '文本框') {questions.push(new TextboxQuestion(element));} else if (element.elementType === '下拉') {//需要将来自服务结果(res.data)的数据推送到optionsquestions.push(new DropdownQuestion(element));console.log("第一步");//我实时调用的服务..//返回 this.http.get(element.optionsUrl).subscribe(res => {//res.data有如下数组,使用foreach推送到elements.options.//[//{ "key": 'average', "value": 'Average' },//{ "key": 'good', "value": 'Good' },//{ "key": 'great', "value": 'Great' }//],//res.data.forEach(result => {console.log("步骤 2");//element.options.push(result);//});//console.log(element.options) 给出上述值 [//{ "key": '平均'...console.log("步骤 3");//console.log(element.options) 给出上述值 [//{ "key": '平均'...//});console.log("步骤 4");//但是这里console.log(element.options)给出了空的}});返回 questions.sort((a, b) => a.order - b.order);}

解决方案

如果将函数 getQuestion 转换为 Observable 的第一步.

为什么有必要?因为您需要调用 this.http.get(element.optionsUrl).这是异步的(所有 http.get 返回可观察的).您需要等待被调用完成才能获取数据.observable 的好处是在订阅函数"里面你有数据.

因此,我们必须认为服务返回可观察对象,组件订阅服务".

好吧,让这个问题.主要问题是我们需要多次调用 http.get.众所周知,对http的所有调用都是异步的,所以如何确保我们拥有所有数据(请记住,我们只有将数据放入订阅函数中.因为我们不想有多个订阅 - 最好是有no subscribe - 在我们的服务中,我们需要使用 forkJoin.ForkJoin 需要一个调用数组,并返回一个结果数组.

所以拳头是创建一个可观察的数组,然后我们返回这个可观察的数组.稍等片刻!我们不想返回一个带有选项的数组,我们想要一个可观察的问题.为此,尽管返回了 observable 数组,但我们返回了一个使用这个 observable 数组的对象.我在回复的底部放了一个简单的例子

getQuestions():Observable{//看到返回一个Observable让问题:任何= [];//首先我们创建一个可观察的数组让 observables:Observable[]=[];this.jsonData.forEach(element => {if (element.elementType === '下拉') {observables.push(this.http.get(element.optionsUrl))}}//如果只想返回一个我们制作的可观察值的forkjoin//返回 forkJoin(observables)//但是我们想要返回一个 Observable 的问题,所以我们使用 pipe(map)) 来转换响应返回 forkJoin(observables).pipe(map(res=>){//这里我们有和数组一样-是的数组数组-//有这么多元素如dowpdown"我们有问题//资源=[//[{ "key": 'average', "value": 'Average' },...],//[{ "key": 'car', "value": 'dog },...],//],//因为我们还有所有的选择,我们可以完成我们的问题让指数=0;this.jsonData.forEach((element) => {//看到有两个参数,//元素和索引"if (element.elementType === '文本框') {questions.push(new TextboxQuestion(element));} else if (element.elementType === '下拉') {//这里我们给element.options赋值element.option=res[index];questions.push(new DropdownQuestion(element));指数++;}})返回问题}))}

注意:关于如何使用of"转换返回值的函数:简单示例

import { of} from 'rxjs';获取数据():任何{让数据={property:"valor"}返回数据;}getObservableData():Observable{让数据={property:"observable"}返回(数据);}getHttpData():Observable{返回 this.httpClient.get("myUrl");}//一个组件可以调用这个函数作为让数据=myService.getData();控制台日志(数据)//看到对getHttpData的调用等于对getObservableData的调用//这是因为我们可以使用of"模拟"一个httpClient.get调用myService.getObservableData().subscribe(res=>{控制台日志(res);}myService.getHttpData().subscribe(res=>{控制台日志(res);}

注意2:forkJoin和map的使用

getData(){让 observables:Observables[];observables.push(of({property:"observable"});observables.push(of({property:"observable2"});返回 (forkJoin(observables).pipe(map(res=>{//在 res 我们有 [{property:"observable"},{property:"observable2"}]res.forEach((x,index)=>x.newProperty=i)//在 res 我们有 [{property:"observable",newProperty:0},//{property:"observable2",newProperty:1}]}))}

更新还有其他方法可以做这些事情.我认为最好有一个返回完整问题"的函数.

//你有jsonData:any=....//所以你可以有一个返回一个可观察的函数jsonData:any=...getJsonData(){返回(this.jsonData)}//好吧,让一个函数返回一个完整的数据怎么样?getFullFilledData(){让 observables:Observables[]=[];this.jsonData.forEach(element => {if (element.elementType === '下拉') {observables.push(this.http.get(element.optionsUrl))}})返回 forkJoin(observables).pipe(map(res=>)让索引 = 0;this.jsonData.forEach((元素) => {if (element.elementType === '下拉') {element.options = res[index];指数++;}})返回 this.jsonData}))}

通过这种方式,您无需更改组件.如果你调用 getFullfilledData 你有(订阅)数据

查看stackblitz

In my angular application, i am in the need to store the data to an array which will be empty at initial stage.

Example:

someFunction() {

 let array = [];

 console.log("step 1");

 this.service.getRest(url).subscribe(result => { 

   result.data.forEach(element => {

   console.log("step 2");

    array.push(element); // Pushing all the objects comes from res.data     

   });

   console.log("step 3");

 });

   console.log("step 4");

}

Here i have listed down the console.log() with step order.

In which the order while calling the function was,

Step 1 Step 4 Step 2 Step 3

Here after step 1, the step 4 calls and later the step 2.. So if i console.log(array) in place of step 4, it gives again empty array..

But in place of step 2 and 3 it gives value.. Coming out of the service the value is empty.

And hence always i am getting empty value in the array.

Kindly help me to store the data to the variable even though there is a time duration of service call and response coming back.

Tried by modifying code for a long time but couldn't get it worked..

Edit:

I have given below the real time application i am currently working with stackblitz link https://stackblitz.com/edit/angular-x4a5b6-ng8m4z

Here in this demo see the file https://stackblitz.com/edit/angular-x4a5b6-ng8m4z?file=src%2Fapp%2Fquestion.service.ts

Where i am using the service call.. If i put async getQuestions() {}, it is giving error of questions.forEach of undefined

In service.ts

    jsonData: any = [
    {
      "elementType": "textbox",
      "class": "col-12 col-md-4 col-sm-12",
      "key": "project_name",
      "label": "Project Name",
      "type": "text",
      "value": "",
      "required": false,
      "minlength": 3,
      "maxlength": 20,
      "order": 1
    },
    {
      "elementType": "textbox",
      "class": "col-12 col-md-4 col-sm-12",
      "key": "project_desc",
      "label": "Project Description",
      "type": "text",
      "value": "",
      "required": true,
      "order": 2
    },
    {
      "elementType": "dropdown",
      "key": 'project',
      "label": 'Project Rating',
      "options": [],
      "order": 3
    }
  ];

  getQuestions() {

    let questions: any = [];

    //In the above JSON having empty values in "options": [],

    this.jsonData.forEach(element => {
      if (element.elementType === 'textbox') {
        questions.push(new TextboxQuestion(element));
      } else if (element.elementType === 'dropdown') {

        //Need to push the data that comes from service result (res.data) to the options

        questions.push(new DropdownQuestion(element));

        console.log("step 1");

      //The service which  i call in real time..

        // return this.http.get(element.optionsUrl).subscribe(res => {

        //res.data has the following array, Using foreach pushing to elements.options.

      //   [
      //   { "key": 'average', "value": 'Average' },
      //   { "key": 'good', "value": 'Good' },
      //   { "key": 'great', "value": 'Great' }
      // ],

        // res.data.forEach(result => {
          console.log("step 2");
        //   element.options.push(result);
        // });
        // console.log(element.options) give values as the above [
      //   { "key": 'average'...
        console.log("step 3");
                // console.log(element.options) give values as the above [
      //   { "key": 'average'...
        // });
        console.log("step 4");
      //But here console.log(element.options) gives empty 
      }
    });

    return questions.sort((a, b) => a.order - b.order);
  }

解决方案

The first step if convert your function getQuestion in an Observable.

Why it is necesary? Because you need call to a this.http.get(element.optionsUrl). This is asyncronous (all http.get return observable). And you need wait to the called is finished to get the data. The good of observable is that inside "subscribe function" you have the data.

Therefore, we must thinking that the "services return observables, the component subscribe to the services".

Well, let the issue. The main problem is that we need several calls to http.get. As we know, all the calls to http are asyncronous, so how can be sure that we have all the data (remember that we only has the data into the subscribe function. As we don't want have several subscribe -the best is have no subscribe- in our service, we need use forkJoin. ForkJoin need an array of calls, and return an array of result.

So the fist is create an array of observable, then we return this array of observable. Wait a moment! we don't want return an array with the options, we want a observables of question. For this, in spite of return the array of observable, we return an object that use this array of observable. I put a simple example at bottom of the response

getQuestions():Observable<any[]> { //See that return an Observable

    let questions: any = [];

    //First we create an array of observables
    let observables:Observable<any[]>[]=[];
    this.jsonData.forEach(element => {
      if (element.elementType === 'dropdown') {
        observables.push(this.http.get(element.optionsUrl))
      }
    }
    //if only want return a forkjoin of observables we make
    //return forkJoin(observables)
    //But we want return an Observable of questions, so we use pipe(map)) to transform the response

    return forkJoin(observables).pipe(map(res=>
    {  //here we have and array like-yes is an array of array-
       //with so many element as "dowpdown" we have in question
       // res=[
       //      [{ "key": 'average', "value": 'Average' },...],
       //        [{ "key": 'car', "value": 'dog },...],
       // ],
       //as we have yet all the options, we can fullfit our questions
       let index=0;
       this.jsonData.forEach((element) => { //see that have two argument, the 
                                                  //element and the "index"
          if (element.elementType === 'textbox') {
             questions.push(new TextboxQuestion(element));
          } else if (element.elementType === 'dropdown') {
               //here we give value to element.options
               element.option=res[index];
               questions.push(new DropdownQuestion(element));
               index++;
          }
       })
       return question
    }))
 }

NOTE: of how convert a function that return a value in observable using "of": Simple example

import { of} from 'rxjs';

getData():any
{
   let data={property:"valor"}
   return data;
}
getObservableData():Observable<any>
{
   let data={property:"observable"}
   return of(data);
}
getHttpData():Observable<any>
{
    return this.httpClient.get("myUrl");
}
//A component can be call this functions as
let data=myService.getData();
console.log(data)
//See that the call to a getHttpData is equal than the call to getObservableData
//It is the reason becaouse we can "simulate" a httpClient.get call using "of" 
myService.getObservableData().subscribe(res=>{
     console.log(res);
}
myService.getHttpData().subscribe(res=>{
     console.log(res);
}

NOTE2: use of forkJoin and map

getData()
{
    let observables:Observables[];

    observables.push(of({property:"observable"});
    observables.push(of({property:"observable2"});

    return (forkJoin(observables).pipe(map(res=>{
        //in res we have [{property:"observable"},{property:"observable2"}]
        res.forEach((x,index)=>x.newProperty=i)
        //in res we have [{property:"observable",newProperty:0},
        //                {property:"observable2",newProperty:1}]
       }))
}

Update There are other way to do the things. I think is better has a function that return the fullfilled "questions".

//You have
jsonData:any=....
//So you can have a function that return an observable
jsonData:any=...
getJsonData()
{
   return of(this.jsonData)
}
//Well, what about to have a function thah return a fullFilled Data?
getFullFilledData()
{
   let observables:Observables[]=[];
   this.jsonData.forEach(element => {
      if (element.elementType === 'dropdown') {
         observables.push(this.http.get(element.optionsUrl))
      }
   })
   return forkJoin(observables).pipe(map(res=>
      let index = 0;
      this.jsonData.forEach((element) => {
      if (element.elementType === 'dropdown') {
         element.options = res[index];
         index++;
      }
   })
   return this.jsonData
   }))
}

In this way you needn't change the component. If you call to getFullfilledData you have (in subscribe) the data

see a stackblitz

这篇关于如何处理服务迟到的数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆