如何处理服务延迟的数据?
How to handle data comes late from service?
在我的 angular 应用程序中,我需要将数据存储到一个在初始阶段为空的数组中。
示例:
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");
}
这里我列出了 console.log()
和步骤顺序。
调用函数的顺序是,
第一步
第 4 步
第2步
第 3 步
这里在第 1 步之后调用第 4 步,然后调用第 2 步。所以如果我 console.log(array)
代替第 4 步,它再次给出空数组..
但是代替 step 2 and 3
它给出了值。从服务中出来的值是空的。
因此我总是在 array
.
中得到空值
请帮助我将数据存储到变量中,即使服务调用和响应返回有一段时间。
修改代码试了很久还是不行..
编辑:
我在下面给出了我目前正在使用的实时应用程序 stackblitz link 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": "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);
}
您的第 4 步不在订阅逻辑范围内。在第3步之后将其移入其中,它将作为最后一个执行。
Observables 发送三种类型的通知:下一步、错误和完成。
https://angular.io/guide/observables
如果你想处理肯定的响应,每个逻辑都必须放在下一个通知中。
myObservable.subscribe(
x => console.log('Observer got a next value: ' + x),
err => console.error('Observer got an error: ' + err),
() => console.log('Observer got a complete notification')
);
如果您有多个可观察对象并想一个接一个地处理它们,您可能也会对像 concatMap 这样的扁平化策略感兴趣。
https://medium.com/@shairez/a-super-ninja-trick-to-learn-rxjss-switchmap-mergemap-concatmap-and-exhaustmap-forever-88e178a75f1b
1-
好吧,一旦有具体的用例,在这里您可以使用不同的方法获得相同的结果,但是通常您可以尝试使用 async await
:
async someFunction() {
this.asyncResult = await this.httpClient.get(yourUrl).toPromise();
console.log("step 4");
}
你不需要再订阅了,一旦从“yourUrl”获取数据,Observable 将被转换为 promise 并且 promise 被解析,然后返回的数据存储在“异步结果”变量。届时将执行最后一个控制台、here you'll find a little use case。
PS: this.httpClient.get(yourUrl)
是在您的 this.service.getRest(url)
中实现的
2-
或者只是将您的 console.log("step 4");
移动到 subscribe
方法范围内以确保顺序。 (Javascript 有一个著名的异步行为,google 它有更多细节)
您的函数正在调用异步 API 调用,因此您将无法在 .subscribe() 函数之前或之后获取数组的值。你需要在函数外声明你的数组。
之后,如果您获取了数据,只需调用另一个函数即可。
let array = [];
someFunction() {
this.service.getRest(url).subscribe(result => {
result.data.forEach(element => {
array.push(element); // Pushing all the objects comes from res.data
});
this.anotherFunction();
});
anotherFunction()
{
console.log(this.array)//you can access it here
}
}
看下面的时间线:
无法保证服务 return 会在第 4 步之前发生,因此无法保证 array
会在第 4 步中填写。
确保使用填充数组的推荐方法是在服务回调中移动数组处理逻辑,这将对应于图片上的第二个向下箭头。
第一步是将函数 getQuestion 转换为 Observable。
为什么需要?因为您需要调用 this.http.get(element.optionsUrl)。这是异步的(所有 http.get return 可观察)。并且需要等待调用完成才能获取数据。 observable 的好处是在 "subscribe function" 里面你有数据。
因此,我们必须认为"services return observables, the component subscribe to the services"。
好吧,让这个问题。主要问题是我们需要多次调用 http.get。正如我们所知,所有对 http 的调用都是异步的,所以如何确定我们拥有所有数据(请记住,我们只有订阅函数中的数据。因为我们不希望有多个订阅 - 最好有没有 subscribe- 在我们的服务中,我们需要使用 forkJoin。ForkJoin 需要一个调用数组,return 一个结果数组。
所以第一步是创建一个observable数组,然后我们return这个observable数组。等一下!我们不想要 return 带有选项的数组,我们想要一个可观察的问题。为此,尽管 return 可观察数组,我们 return 使用此可观察数组的对象。我在响应的底部放了一个简单的例子
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
}))
}
注意:关于如何使用 "of" 将 return 值转换为可观察值的函数:简单示例
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);
}
注意 2:使用 forkJoin 和 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}]
}))
}
更新
还有其他方法可以做这些事情。我认为更好的是有一个功能 return 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
}))
}
这样就不用改动组件了。如果你打电话给 getFullfilledData 你有(订阅)数据
看到一个stackblitz
在我的 angular 应用程序中,我需要将数据存储到一个在初始阶段为空的数组中。
示例:
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");
}
这里我列出了 console.log()
和步骤顺序。
调用函数的顺序是,
第一步 第 4 步 第2步 第 3 步
这里在第 1 步之后调用第 4 步,然后调用第 2 步。所以如果我 console.log(array)
代替第 4 步,它再次给出空数组..
但是代替 step 2 and 3
它给出了值。从服务中出来的值是空的。
因此我总是在 array
.
请帮助我将数据存储到变量中,即使服务调用和响应返回有一段时间。
修改代码试了很久还是不行..
编辑:
我在下面给出了我目前正在使用的实时应用程序 stackblitz link 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": "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);
}
您的第 4 步不在订阅逻辑范围内。在第3步之后将其移入其中,它将作为最后一个执行。
Observables 发送三种类型的通知:下一步、错误和完成。 https://angular.io/guide/observables 如果你想处理肯定的响应,每个逻辑都必须放在下一个通知中。
myObservable.subscribe(
x => console.log('Observer got a next value: ' + x),
err => console.error('Observer got an error: ' + err),
() => console.log('Observer got a complete notification')
);
如果您有多个可观察对象并想一个接一个地处理它们,您可能也会对像 concatMap 这样的扁平化策略感兴趣。 https://medium.com/@shairez/a-super-ninja-trick-to-learn-rxjss-switchmap-mergemap-concatmap-and-exhaustmap-forever-88e178a75f1b
1-
好吧,一旦有具体的用例,在这里您可以使用不同的方法获得相同的结果,但是通常您可以尝试使用 async await
:
async someFunction() {
this.asyncResult = await this.httpClient.get(yourUrl).toPromise();
console.log("step 4");
}
你不需要再订阅了,一旦从“yourUrl”获取数据,Observable 将被转换为 promise 并且 promise 被解析,然后返回的数据存储在“异步结果”变量。届时将执行最后一个控制台、here you'll find a little use case。
PS: this.httpClient.get(yourUrl)
是在您的 this.service.getRest(url)
2-
或者只是将您的 console.log("step 4");
移动到 subscribe
方法范围内以确保顺序。 (Javascript 有一个著名的异步行为,google 它有更多细节)
您的函数正在调用异步 API 调用,因此您将无法在 .subscribe() 函数之前或之后获取数组的值。你需要在函数外声明你的数组。
之后,如果您获取了数据,只需调用另一个函数即可。
let array = [];
someFunction() {
this.service.getRest(url).subscribe(result => {
result.data.forEach(element => {
array.push(element); // Pushing all the objects comes from res.data
});
this.anotherFunction();
});
anotherFunction()
{
console.log(this.array)//you can access it here
}
}
看下面的时间线:
无法保证服务 return 会在第 4 步之前发生,因此无法保证 array
会在第 4 步中填写。
确保使用填充数组的推荐方法是在服务回调中移动数组处理逻辑,这将对应于图片上的第二个向下箭头。
第一步是将函数 getQuestion 转换为 Observable。
为什么需要?因为您需要调用 this.http.get(element.optionsUrl)。这是异步的(所有 http.get return 可观察)。并且需要等待调用完成才能获取数据。 observable 的好处是在 "subscribe function" 里面你有数据。
因此,我们必须认为"services return observables, the component subscribe to the services"。
好吧,让这个问题。主要问题是我们需要多次调用 http.get。正如我们所知,所有对 http 的调用都是异步的,所以如何确定我们拥有所有数据(请记住,我们只有订阅函数中的数据。因为我们不希望有多个订阅 - 最好有没有 subscribe- 在我们的服务中,我们需要使用 forkJoin。ForkJoin 需要一个调用数组,return 一个结果数组。
所以第一步是创建一个observable数组,然后我们return这个observable数组。等一下!我们不想要 return 带有选项的数组,我们想要一个可观察的问题。为此,尽管 return 可观察数组,我们 return 使用此可观察数组的对象。我在响应的底部放了一个简单的例子
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
}))
}
注意:关于如何使用 "of" 将 return 值转换为可观察值的函数:简单示例
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);
}
注意 2:使用 forkJoin 和 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}]
}))
}
更新 还有其他方法可以做这些事情。我认为更好的是有一个功能 return 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
}))
}
这样就不用改动组件了。如果你打电话给 getFullfilledData 你有(订阅)数据
看到一个stackblitz