Angular 7-Promise解析后,promise中的forEach迭代正在执行。为什么?

时间:2019-04-12 00:03:43

标签: javascript angular typescript promise

我创建了一个服务,用于某些需要在调用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");
    }
}

2 个答案:

答案 0 :(得分:1)

您似乎不完全了解代码的哪些部分是异步的,以及代码的部分将以什么顺序执行。

编辑:我假设您代码中的所有可观察对象都是异步的,即它们执行某种API调用以获取所需的数据。它们可能是同步的,但您的代码实际上不应该假定那样。如果产品生命周期后期的同步调用变为异步,这将大大降低破坏某些内容的风险。 END EDIT

因此,您要解决的直接问题是,您要解决订阅之外的承诺-因此,在进入forEach循环之前。因此,时间轴是这样的:

  • PollComponent呼叫firebaseService.initPoll();
  • Promise被创建并返回到PollComponent;
  • PollComponent遵守诺言;
  • 承诺中的Lambda开始执行;
  • 您要求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的函数,则它将被执行。