无法将异步函数的结果保存到对象

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.keysObject.valuesArray#reduce,您还可以使用Object.entriesObject.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.entriesObject.fromEntries 不可用,您可以随时恢复为 .values.keysArray#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中的变量[重复]

使用 Node.js 和 Mongoose 将查询结果保存到模型/模式的属性

将经纬度保存在对象中

从向量中保存的类对象输出类函数

异步函数未保存在数据库中

条码扫描器Phonegap插件;无法将结果保存到全局变量中,或保存到隐藏字段