无法将异步函数的结果保存到对象
Posted
技术标签:
【中文标题】无法将异步函数的结果保存到对象【英文标题】:Can't save result from async function to object 【发布时间】:2021-06-21 04:43:08 【问题描述】:如果我知道如何解决它,我在家里。 我现在的问题是保存异步函数的结果。 如果我在该函数之后执行console.log,那么我将收到翻译后的值,但片刻之后,当我检查我的动词在打包成对象后的样子时,我将收到未翻译的值。有人知道为什么吗?
private translate(file: any)
const keys = Object.keys(file)
const values = Object.values(file) // [!] - English Value at start, it's ok
values.forEach( async (value, index) =>
if( typeof(value) === 'object')
value = this.translate(value)
else
const translateResult = await this.GetTranslatedByResstring(keys[index], 1045).toPromise()
value = translateResult.getPhrase()
console.log(value) // [!] <- Polish Value, it's ok
)
const zipObj = xs => ys => xs.reduce( (obj, x, i) => ( ...obj, [x]: ys[i] ), )
const obj = zipObj (keys) (values)
console.log(obj) // [!] <- English Value, it's not ok, i need Polish value inside
return obj
#更新 1
调用翻译的地方:
public async getTranslatedJson(
sourceFile: File,
originLanguage: Language,
destinatonLanguage: Language
): Promise<string>
const file = await this.getFile(sourceFile)
const parsedFile = JSON.parse(file)
const translatedFile = this.translate(parsedFile)
return null
#更新 2
我的 getFile 函数:
private getFile(
sourceFile: File
): Promise<string>
return new Promise((resolve) =>
const file = sourceFile[0]
const fileReader = new FileReader();
fileReader.onloadend = (e) =>
const testResult = fileReader.result.toString();
resolve(testResult);
fileReader.readAsText(file, "UTF-8");
);
【问题讨论】:
忽略.forEach()
不关心async
回调的事实......为什么还有.forEach()
? value
只会得到 values
中最后一个“值”的结果
我只需要翻译该对象中的每个元素,我不在乎它是 foreach、for 还是其他类型的循环,我只需要翻译它们
脚本中的任何内容都不会尝试更改values
的内容。看看Promise.all()
和How do I return the response from an asynchronous call?
我尝试做任何事情但我厌倦了它,我工作了大约 1 个月。
或许不需要将 observable 转换为 promise? translate()
函数是如何以及在哪里调用的?调用者如何期望响应?
【参考方案1】:
从我的评论扩展,我认为没有必要在这里将 observable 转换为 Promise。您可以改为返回一个 RxJS forkJoin
函数,该函数并行触发多个可观察对象,并在需要响应的地方订阅它。
除了使用Object.keys
、Object.values
和Array#reduce
,您还可以使用Object.entries
和Object.fromEntries
。
试试下面的
import of, forkJoin from 'rxjs';
import map from 'rxjs/operators';
private translate(file: any): Observable<any> // <-- return `Observable` here
return forkJoin( // <-- `forkJoin` to trigger sources in parallel
Object.entries(file).map(([key, value]) => // <-- `Object.entries` with `Array#map`
if (typeof (value) === 'object')
return of([key, this.translate(value)]); // <-- use `of` to convert value to observable
return this.GetTranslatedByResstring(value, 1045).pipe(
map((res: any) => ([key, res.getPhrase()])) // <-- map to the required format [key, value]
);
)
).pipe(
map((res: any) => Object.fromEntries(res)) // <-- convert back to object
);
现在由于该函数返回一个 observable,您需要在需要响应的地方订阅它。
例如。
ngOnInit()
this.translate(file).subscribe(
next: (value: any) =>
// any statements that depend on the response must be here
,
error: (error: any) =>
// handle error
)
更新 1
回到我的第一点,我认为这里没有必要将 observable 转换为 promise。因此,请避免 this.getFile()
函数中的 toPromise()
并从那里返回 observable。现在在getTranslatedJson()
函数中,您可以使用像switchMap
这样的高阶映射运算符从一个可观察对象映射到另一个对象。
public getTranslatedJson(
sourceFile: File,
originLanguage: Language,
destinatonLanguage: Language
): Observable<any>
return this.getFile(sourceFile).pipe(
switchMap((file: any) =>
this.translate(JSON.parse(file))
)
);
在这种情况下,您将订阅需要响应的 getTranslatedJson()
函数。
ngOnInit()
this.getTranslatedJson(file, origLang, destLang).subscribe(
next: (value: any) =>
// any statements that depend on the response must be here
,
error: (error: any) =>
// handle error
)
更新 2
如果由于某种原因 Object.entries
和 Object.fromEntries
不可用,您可以随时恢复为 .values
、.keys
和 Array#reduce
,就像您最初尝试的那样。
private translate(file: any): Observable<any> // <-- return `Observable` here
return forkJoin( // <-- `forkJoin` to trigger sources in parallel
Object.values(file).map((value: any) => // <-- `Object.values()` with `Array#map`
if (typeof (value) === 'object')
return of(this.translate(value)); // <-- use `of` to convert value to observable
return this.GetTranslatedByResstring(value, 1045).pipe(
map((res: any) => res.getPhrase()) // <-- map to the result from `getPhrase()`
);
)
).pipe(
map((res: any) => // <-- convert back to object
Object.keys(file).reduce((acc, curr, i) =>
( ...acc, [curr]: res[i] ),
Object.create(null)
);
)
);
更新 3
通过调整以前的答案,您可以为琐碎的任务找到大多数解决方案。
// credit: https://***.com/a/48802804/6513921
private getFile(
sourceFile: File
): Observable<any>
const file = sourceFile[0];
const fileReader = new FileReader();
return Observable.create((observer: Subscriber<any>): void =>
fileReader.onloadend = ((ev: any): void =>
observer.next(fileReader.result.toString());
observer.complete();
);
fileReader.onerror = (error: any): void =>
observer.error(error);
);
fileReader.readAsText(file, "UTF-8");
【讨论】:
如何修复它:“ObjectConstructor”类型上不存在属性“fromEntries”。您需要更改目标库吗?尝试更改lib
编译器选项。
@Obyi: ***.com/a/45422950/6513921
我将它添加到 webconfig 但仍然出现错误,即使我尝试以该线程中提供的其他方式修复它
我们触及了我帖子后面的另一个功能,所以我更新了我的问题,因为我无法在我的 getfile 上进行管道和映射
当我研究了你的回复时,我认为它不会起作用。为什么?我告诉你用例。您上传 JSON 文件,在此之后,您的 JSON 由 Web 应用程序翻译,在此 JSON 需要准备好下载之后,所以最后一步制作问题,因为我订阅了内部服务,所以我的组件中不会有任何翻译我下载了我的新 json 文件。最简单的方法是蛮力将短语翻译成我的对象,但我仍然不知道该怎么做。以上是关于无法将异步函数的结果保存到对象的主要内容,如果未能解决你的问题,请参考以下文章
Firebase onCall函数将结果保存到android中的变量[重复]