Angular 9 машинопись как сделать так, чтобы функция с циклом for завершалась первой, прежде чем переходить к следующей функции?

#angular #typescript #es6-promise #asynchronous-javascript

#angular #машинопись #es6-обещание #асинхронный-javascript

Вопрос:

В настоящее время я больше узнаю об асинхронном аспекте angular и typescript, поэтому простите меня, если моих знаний не хватает, в настоящее время я сталкиваюсь с проблемой, когда я пытаюсь реализовать обещания сделать функцию, которая содержит цикл for, завершенной первой перед вызовом следующей функции,

Вот пример кода, цель которого состоит в том, чтобы у меня был цикл for для прохождения всех форм и в цикле, вызвать службу, чтобы получить необходимые данные для помещения в объект, затем приступить к созданию нового объекта с указанными данными и помещению вновь созданных данных в массив, который после завершения этой функции и запуска следующей функции отправит массив в службу.

Например, у меня есть форма размером 2, я бы хотел, чтобы цикл for завершился перед вызовом последнего resolve() для возврата к submitObj() и, наконец, вызова sendTestObject(),

в component.ts

  submitObj() {

    // call function to create object and wait for it to finish
    // via a promise.then()
    this.createTestObject().then((){

        //then finally call another service to give created object
        this.sendTestObject();
    });
    
}
 
 
 createTestObject() {
    const testObjList: testActvity[] = [];
    let count = 1;
    return new Promise ((resolve) => {

    // How do I make it so that this for loop goes in order
    // and finishes first
    // before going to the next line of code?
    for (const i in this.testFormArray.controls) {
      testObj = new Actvity();
      let promise = new Promise((resolve,reject) =>{
      
      this.testService.getTestData().then(res => {
        console.log(count);
        if (res) {
            if (res.results) {
                this.testArrayForm.controls[i].get('testForm').patchValue({
                    "Id": res.results.Id,
                    "type": res.results.type,
                })
            }
        }      
      })}
      
      resolve();
    }
  });
  
  promise.then(()=> {
     console.log("in then() of "   count);
     testObj.testForm = this.testArrayForm.controls[i].get('testForm').value;
     testObjList.push(testObj);
     count   ;
  })

  //How do I wait for the for loop to finish first before running this?
  console.log("FINISHED)"
  resolve();
}
  

в service.ts

 getTestData(): Promise<any>{
    let res = this.http.get("testData.json")
    .toPromise()
    .then( res => {
      return res ;
    }). catch (err => {
        return err ;
    });
     return new Promise ((resolve, reject) => {
       resolve(res);
     });
 }
  

Ответ №1:

Вот мое решение. Я не использую ваш код, потому что не могу его протестировать, но я показываю общую идею. По сути, я избавляюсь от for цикла и вместо итерации элементов (в вашем случае входных данных формы) создаю рекурсивную цепочку if promises . Как только последняя разрешится, мы знаем, что обработали все элементы:

 const data = ['foo', 'bar'];

function processElement(index: number): void {
    console.log('do something with element: '   index   ' which is: '   data[index]);
}

function processAllElements(index: number, count: number): Promise<void> {
    return new Promise((resolve, reject) => {
        if(index == count) {
            resolve();
            return;
        }
        
        processElement(index);
        
        return processAllElements(index   1, count);
    });
}

function run(): void {
    processAllElements(0, data.length)
        .then(() => {
            console.log('All done');
        });
}

run();
  

Ответ №2:

почему бы не использовать «concat» оператор rxjs (Angular хорошо работает с наблюдаемыми без необходимости использования Promise)? Простой пример:

Если у нас есть функция, которая возвращает наблюдаемое:

 obs(value){
   return of(value).pipe(delay(1000))
}
  

И объединить массив наблюдаемых

 const $concatObs=concat(...[0,1,2,3].map(x=>this.obs(x)));
  

Мы можем подписаться

 $concatObs.subscribe(res=>{
      this.value=res;
})
  

Простой stackblitz

Обновите «ошибку cacth». Проблема с concat, если наша функция может выдавать ошибку. ну, мы можем использовать catchError (другой оператор rxjs), чтобы учесть это.

Если мы «передадим» наблюдаемые, чтобы сделать так, чтобы при возникновении ошибки возвращался объект {error:....}

 const $concatObs = concat(
  ...[0, 1, 2, -2, 3, 4,"finished"].map(x => this.obs(x)
     .pipe(catchError(error=>{
          return of({error:error})
      })))
);
  

В subscribe мы можем проверить, есть ли ошибка

 $concatObs.subscribe(res => {
  if (res.error)
  {
    console.log(res.error)
  }else
    this.value = res;
});