#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;
})
Обновите «ошибку 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;
});