Как сделать так, чтобы typescript выполнялся синхронно?

#typescript #async-await #synchronization

#typescript #асинхронный-ожидание #синхронизация #асинхронное ожидание

Вопрос:

Я пытался реализовать функцию загрузки изображений, и у меня есть класс, как указано ниже.

 class myClass {
  oriData: any;
  name: string;

  constructor(props) {
    this.name = props.name;
    this.oriData = this.readFile(props);
  }

  async readFile(props) {
    return await new Promise((resolve, reject) => {
      let reader = new FileReader();
      reader.readAsDataURL(props);
      reader.onload = () => {
        let result = reader.result;
        resolve(result);
      };
      reader.onerror = reject;
    });
  }
}

private async process(file): Promise<myClass> {
  try {
    let image = await new myClass(file);
    console.log(image.oriData);
    console.log(image.name);
    return Promise.resolve(image);
  }
  catch(err) {
    console.log(err);
  }
}
  

Однако, когда я пытаюсь получить image.oriData и image.name , image.oriData отображается как неопределенный, но другой является правильным. Я проверяю шаг и обнаруживаю, что процесс (файл) все равно создаст экземпляр MyClass, не дожидаясь завершения reader.onload. Я считаю, что это должно быть проблемой синхронизации. Кто-нибудь может помочь мне улучшить этот код? Спасибо!

Ответ №1:

Вы можете вернуть буквально что угодно из конструктора JavaScript, так что это возможный шаблон:

 class DontDoThis {
  constructor(props) {
    this.name = props.name;
    const results = this.readFile(props);
    // As a side-effect of the promise completing, set a field
    results.then(data => this.oriData = data);

    // Force this constructor to be treated as async
    // even though we cannot say `async constructor`
    return results.then(() => this, () => this);
  }
}

async function process(...) {
  const complexData = await new DontDoThis(...);
}
  

Тем не менее, намного, намного лучше просто иметь функцию, которая создает конечное значение:

 interface ImageData { name: string, oriData: any };
// This can also be a static method, e. g. `ImageData.create(...)`
async function ComplexData(props): Promise<ImageData> {
  return readFile(props).then(data => ({name: props.name, oriData: data}));
}

async function process(props) {
  const complexData = await ComplexData(props);
}
  

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