微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

javascript-承诺then()方法未按预期触发?

这个问题已经在这里有了答案:            >            Using async/await with a forEach loop                                    14个
在下面的代码中,我希望mark-1在mark-2之前启动.我猜我在某些地方没有使用await和async正确.

我很确定这一行:

  const things = await fs.promises.readdir(folder);

这条线

  const stats = await fs.promises.stat(path);

是正确的,因为我们正在等待文件系统做出响应.

到目前为止,我还不担心错误检查或跨平台代码,只是保证了正确工作

// Libraries
const fs = require('fs');

// API
getThings('_t/').then(() => {
  console.log('mark-2')
})

// Accesses the file system and returns an array of files / folders
async function getThings (folder) {
  const things = await fs.promises.readdir(folder);
  things.forEach(async (thing)=>{
    await getStats(thing, folder);
  });
}

// Gets statistics for each file/folder
async function getStats (thing, folder) {
  const path = folder + thing;
  const stats = await fs.promises.stat(path);
  console.log('mark-1');
}

解决方法:

问题是您在forEach调用中使用async和await,因此无法按预期工作.

forEach方法实际上并不关心回调函数的返回值(在这种情况下,getStats返回的承诺).

您应该将事物数组映射到Promise,并使用Promise.all:

async function getThings (folder) {
  const things = await fs.promises.readdir(folder);
  const promises = things.map(thing =>  getStats(thing, folder));
  return await Promise.all(promises);
}

请注意,这将“并行”执行承诺,而不是顺序执行.

如果要逐个安全地执行promise,则可以“减少” promise数组,也可以使用常规循环(for-for-of).

编辑:

让我尝试阐明为什么对forEach使用异步回调函数不起作用.

尝试1:将其脱糖:

在原始示例中,我们有以下内容

// ...
things.forEach(async (thing)=>{
  await getStats(thing, folder);
});
// ...

如果我们将回调与forEach分开,则我们可以:

const callback = async (thing)=>{
    await getStats(thing, folder);
};

things.forEach(callback);

如果我们“取消糖化”异步功能

const callback = function (thing) {
  return new Promise((resolve, reject) => {
    try {
      getStats(thing, folder).then(stat => /*do nothing*/);
    } catch (err) {
      reject(err);
    }
    resolve();
  });
};

things.forEach(callback);

用async标记一个函数可确保该函数将始终返回一个promise,而不管其完成情况如何,如果该函数在执行时没有明确的返回值,则该promise将以undefined解析,如果它返回某个值,则该promise将解析为最后,如果函数中有东西抛出,则诺言将被拒绝.

正如您所看到的,问题是承诺没有被任何东西等待,而且它们也没有解决任何价值.放在回调中的等待实际上不使用值,就像上面我在做.then一样,不使用值.

尝试2:实现简单的forEach函数

function forEach(array, callback) {
  for(const value of array) {
    callback(value);
  }
}

const values = [ 'a', 'b', 'c' ];
forEach(values, (item) => {
  console.log(item)
});

上面的forEach是Array.prototype.forEach方法的简化,只是为了显示其结构,实际上,回调函数被称为将数组作为this值传递,并传递三个参数,即当前元素,当前索引和再次使用数组实例,但我们明白了.

如果我们要实现异步forEach函数,则必须等待回调调用

const sleep = (time, value) => new Promise(resolve => setTimeout(resolve(value), time));
const values = [ { time: 300, value: 'a'}, { time: 200, value: 'b' }, {time: 100, value: 'c' } ];

async function forEachAsync(array, callback) {
  for(const value of array) {
    await callback(value);
  }
}

(async () => {

  await forEachAsync(values, async (item) => {
    console.log(await sleep(item.time, item.value))
  });
  
  console.log('done');
  
})()

上面的forEachAsync函数将逐项依次迭代并等待,通常您不希望这样做,如果异步函数是独立的,则可以并行完成它们,就像我最初建议的那样.

const sleep = (time, value) => new Promise(resolve => setTimeout(resolve(value), time));
const values = [ { time: 300, value: 'a'}, { time: 200, value: 'b' }, {time: 100, value: 'c' } ];

(async () => {

  const promises = values.map(item  => sleep(item.time, item.value));
  const result = await Promise.all(promises);
  console.log(result);
})()

如您所见,即使promise是并行执行的,我们也将以与数组中promise相同的顺序获得结果.

但是此示例与第一个示例的区别在于,此示例仅花费300毫秒(最长的解决承诺),而第一个示例则花费了600毫秒(300毫秒200毫秒100毫秒).

希望它使它更清晰.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐