【问题标题】:Promise .then() chains: second .then run before the first?? o.0Promise .then() 链:第二个 .then 在第一个之前运行? o.0
【发布时间】:2019-09-29 21:46:06
【问题描述】:

我正在尝试从 .then 结构中的 blob 数据创建一个新的 File 对象,然后在第二个 .then 中读取该文件的信息。

但是第二个在第一个结束之前运行,所以文件对象还没有被填充。

这是正常行为吗?我应该创建一个异步函数,在第一个函数中调用,然后确保第二个函数在之后被严格调用吗?

      let output = {file: {}, file_infos: {}},
          image = FileAPI.Image(src_file);
      await Promise.all(Object.keys(parameters).map(async (parameter_name) => { // Pass file threw all modifiers (resizing, rotation, overlaying)
        try {
          image = await FileMethods[parameter_name](image, parameters[parameter_name]);
            return image;
        }
        catch(err) {
          console.log(err);
        };
      }))
      .then((output_image) => {
        output_image[0].toBlob((blob) => {
          output.file = new File([blob], src_file.name); // Need this to be fullfilled before step2
          console.log('step1');
        });
      })
      .then(() => {
        console.log('step2');
        FileAPI.getInfo(output.file, (err/**String*/, infos/**Object*/) => {
          if( !err ){
            output.file_infos = infos;
          } else {
            console.log("this is triggered because output.file isn't filled yet");
          }
        })
      });
      // console.log(output);
      return output;

控制台显示:

step2 this is triggered because output.file isn't filled yet step1

感谢您的帮助:)

【问题讨论】:

  • 已有正确答案。只是想说如果你使用 async-await 而不是 promise then 链,那将是一个很好的补充。
  • 将 await 与 .then 混合并不是最佳做法

标签: javascript file asynchronous promise async-await


【解决方案1】:

两个 .then 中的两个异步函数不返回 Promise,所以首先它们需要“Promisified”,因为你已经在使用 async/await,所以不要使用 Promise .then 链

const image = FileAPI.Image(src_file);
const output_image = await Promise.all(Object.keys(parameters).map(async(parameter_name) => {
    try {
        image = await FileMethods[parameter_name](image, parameters[parameter_name]);
        return image;
    } catch (err) {
        console.log(err);
    };
}));
const file = await new Promise((resolve, reject) => output_image[0].toBlob((blob) => 
    resolve(new File([blob], src_file.name))
));
const file_infos = await new Promise((resolve, reject) => FileAPI.getInfo(file, (err, file_infos) => {
    if (!err) {
        resolve(file_infos);
    } else {
        reject("this is triggered because output.file isn't filled yet");
    }
));
return {file, file_infos};

关于

const output_image = await Promise.all(Object.keys(parameters).map(async(parameter_name) => {
    try {
        image = await FileMethods[parameter_name](image, parameters[parameter_name]);
        return image;
    } catch (err) {
        console.log(err);
    };
}));

你实际上是在做return await FileMethods[parameter_name](image, parameters[parameter_name]) - 所以,在这种情况下你真的不需要异步/等待模式,只需在 .map 中返回 Promise

const output_image = await Promise.all(Object.keys(parameters).map((parameter_name) => 
    FileMethods[parameter_name](image, parameters[parameter_name]);
));

或者,甚至更好(在我看来)

const output_image = await Promise.all(Object.entries(parameters).map((p_name, p_value) =>
    FileMethods[p_name](image, p_value)
));

或者,使用 Promise .then 链而不使用 async/await

const image = FileAPI.Image(src_file);
return Promise.all(Object.keys(parameters).map(parameter_name => FileMethods[parameter_name](image, parameters[parameter_name])))
.then(output_image => new Promise((resolve, reject) => output_image[0].toBlob((blob) => 
    resolve(new File([blob], src_file.name))
)))
.then(file => new Promise((resolve, reject) => FileAPI.getInfo(file, (err, file_infos) => {
    if (!err) {
        resolve({file, file_infos});
    } else {
        reject("this is triggered because output.file isn't filled yet");
    }
)));

【讨论】:

  • 谢谢!我知道我可以通过新的承诺解决问题,但我对多层次的承诺还不太满意。我同意你的“imo”信息,它更好!感谢您的宝贵时间!
  • 搜索 Object.entries。这不是有效的方式(在我的情况下),因为它不能保证顺序,并且出于某些优化原因我需要这样做。例如,列表中的第一个参数始终是“resize”,因此旋转、变换、叠加或任何较小的图像更有效。我正在与服装的网络商家合作,他们的照片可能真的很大 XD
  • 然而,您使用 Object.keys @Romain'Maz'BILLOIR - 并且 object.keys/values/entries 都以与 for...in 相同的顺序返回
【解决方案2】:

.toBlob() 立即返回,因为它使用异步回调模式。

您想要的是返回一个在工作完成时解决的承诺。所以你可以这样做:

.then((output_image) => {
    return new Promise((res, rej) => {
      output_image[0].toBlob((blob) => {
        output.file = new File([blob], src_file.name); // Need this to be fullfilled before step2
        console.log('step1');
        res();
      });
    });
  })

【讨论】:

  • 可能需要为 FileAPI.getInfo 做同样的事情 - 如果你希望返回输出返回所有内容
【解决方案3】:

toBlob 可能是异步的。将第一个 .then 更改为:

.then((output_image) => {
  return new Promise((resolve) => output_image[0].toBlob((blob) => {
    output.file = new File([blob], src_file.name); // Need this to be fullfilled before step2
    console.log('step1');
    resolve();
  }));
})

【讨论】:

  • @Bergi 通常我会同意。但是在 OP:s 代码中 output 是一个全局变量,下面的 .then 不需要参数。所以在这种情况下,不,它不应该是resolve(output)。
  • 可能需要对FileAPI.getInfo 执行相同操作 - 如果您希望返回输出返回所有内容
猜你喜欢
  • 2022-01-20
  • 2016-05-13
  • 1970-01-01
  • 2020-10-09
  • 2014-07-19
  • 2019-02-22
  • 2018-07-06
  • 2019-03-07
  • 1970-01-01
相关资源
最近更新 更多