我创建了一个服务,用于某些需要在调用drawPoll()函数之前进行的操作。我添加了控制台日志来跟踪执行顺序,但无法弄清楚为什么链接到.then()的函数为什么在promise内部的forEach迭代完成之前执行。创建服务并在Promise中包装forEach操作的全部目的是使我可以完全确定forEach迭代已在调用drawPoll()函数之前完成。我在这里想念什么?
poll.component.ts
firebase.service.ts
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Define a doubly linked list type
typedef struct node {
char name[100];
int age;
float weight;
struct node *next;
} node;
void print_list(node* list, int count) {
int j = 0;
node temp;
// walk the list to print out the contents
for (int i = 0; i < count; i++) {
j = i - 1;
while (j >= 0 && strcmp(list[j + 1].name, list[j].name) < 0) {
temp = list[j + 1];
list[j + 1] = list[j];
list[j] = temp;
j--;
}
}
while (list) {
printf("%s%d\n%f\n", list->name, list->age, list->weight);
list = list->next;
}
printf("\n");
}
node* new_node(char *value, int a, float w) {
node* t = (node *)malloc(sizeof(node));
strcpy(t->name, value);
t->age = a;
t->weight = w;
t->next = NULL;
return t;
}
node* add(node* list) {
node* t = list;
char name[100];
int a;
float w;
printf("Enter name: ");
fgets(name, 100, stdin);
printf("Enter age: ");
scanf("%d", &a);
printf("Enter weight: ");
scanf("%f", &w);
while (getchar() != '\n');
node* s = new_node(name, a, w);
// special case: starting with an empty list
if (t == NULL)
return s;
s->next = list;
return s;
}
int getChoice() {
int ch;
printf("1. Add a Record\n2. Display All Records\n");
printf("3.Quit\nEnter choice: ");
scanf("%d", &ch);
while (getchar() != '\n');
return ch;
}
int main() {
node* my_list = NULL;
int ch;
int count = 0;
while ((ch = getChoice()) != 3) {
if (ch == 1) {
my_list = add(my_list);
count++;
}
else if (ch == 2) {
print_list(my_list, count);
}
printf("\n");
}
}
答案 0 :(得分:1)
您似乎不完全了解代码的哪些部分是异步的,以及代码的部分将以什么顺序执行。
编辑:我假设您代码中的所有可观察对象都是异步的,即它们执行某种API调用以获取所需的数据。它们可能是同步的,但您的代码实际上不应该假定那样。如果产品生命周期后期的同步调用变为异步,这将大大降低破坏某些内容的风险。 END EDIT
因此,您要解决的直接问题是,您要解决订阅之外的承诺-因此,在进入forEach
循环之前。因此,时间轴是这样的:
PollComponent
呼叫firebaseService.initPoll()
; Promise
被创建并返回到PollComponent
; PollComponent
遵守诺言; getChoices()
是可观察的,创建一些管道并进行订阅,我相信这是您开始困惑的地方: subscribe()
不会立即触发任何结果,并且不会触发任何结果等待执行可观察的管道和订阅lambda中应该执行的所有操作。因此,您已经订阅了管道,并立即继续执行其余的promise lambda的代码。Promise
得到解决。 Observable甚至还没有开始做任何事情,但是您已经解决了promise,它会立即触发then()
订阅链。这是您的then()
lambda执行,然后所有内容冷却一段时间的时候。Observable
发出一个事件,该事件进入您的订阅并触发forEach
周期,但为时已晚,要发出您希望从可观察到的内容中获取任何东西,因为Promise
已解决。但是,另一方面,这似乎是代码中不同步发生的几件事之一。例如,在foreach内部,您对this.getVotes(choiceKey)
管道进行了两次订阅,并且第一个订阅将某些内容推送到choices
集合中,而该集合被第二个订阅所使用-再次完全不同步,因为它们不同步调用subscribe()
时立即执行。因此,您需要以这种方式链接呼叫,以便以后的步骤只能在前面的步骤之后进行。
现在,我想起了自己的位置,首先想到的通常是这样的:“好吧,我只需要重新排列我的订阅并将稍后的订阅放入先前的订阅中”。这很明显是错误的。 :) Rx的整个想法是,您应该仅订阅整个管道的最终结果,该结果通常发生在创建该管道的服务之外。因此,重新排列代码的正确方法是使用pipe()
,switchMap()
,flatMap()
,combineLatest()
,merge()
,{{1 }}等Rx运算符,这样一来,您可以通过逐步进行此管道操作来最终生成您真正需要的单个结果,而无需在您使用的任何单个map()
上显式调用subscribe()
此外,您不必手动创建Observable
,实际上在可观察对象上确实有一个简单的运算符可用于此任务。
我不知道这是否是您所用的正确代码,但以下内容是您如何使用所描述的方法重新排列内容的想法。我只希望它足够清楚,可以演示如何根据您的情况用不同的管道运算符替换订阅。
Promise
答案 1 :(得分:0)
虽然我没有足够的源代码来确认这些功能的特定行为,但以下代码中的pipe
和肯定的subscribe
可能会将forEach
推向了异步状态执行:
this.getChoices(pollKey).pipe(first()).subscribe(fetchedChoices => {
fetchedChoices.forEach(choice => {...
fetchedChoices => {fetchedChoices.forEach(...
定义了订阅函数的回调函数,该回调函数将在Promise执行程序函数的执行之外发生。 resolve(choices, votedChoice)
将在调用subscribe
之后且回调传递给subscribe
之前立即执行。 forEach
代码在回调函数中进行订阅,并且将被异步调用(并在Promise解析之后)。
并非所有回调都是异步执行的,但是最好将如果将一个回调传递给名为subscribe
的函数,则它将被执行。