为了账号安全,请及时绑定邮箱和手机立即绑定

Promise then() 方法没有按预期触发?

Promise then() 方法没有按预期触发?

暮色呼如 2021-06-02 14:50:34
在下面的代码中,我希望mark-1在mark-2. 我猜,我不使用await和async正确的一些地方。我很确定这一行:  const things = await fs.promises.readdir(folder);和这条线  const stats = await fs.promises.stat(path);是正确的,因为我们正在等待文件系统响应。到目前为止,我并不关心错误检查或跨平台代码,只是让承诺正常工作// Librariesconst fs = require('fs');// APIgetThings('_t/').then(() => {  console.log('mark-2')})// Accesses the file system and returns an array of files / foldersasync function getThings (folder) {  const things = await fs.promises.readdir(folder);  things.forEach(async (thing)=>{    await getStats(thing, folder);  });}// Gets statistics for each file/folderasync function getStats (thing, folder) {  const path = folder + thing;  const stats = await fs.promises.stat(path);  console.log('mark-1');}
查看完整描述

1 回答

?
沧海一幻觉

TA贡献1824条经验 获得超5个赞

问题是,你正在使用async,并await在forEach通话,而这也不行,你期望的那样。


该forEach方法并不真正关心回调函数的返回值(在本例中是getStats返回的 promise )。


你应该承诺map的things数组,并使用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);

}

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


如果您想一个接一个地依次执行承诺,您可以“减少”承诺数组,或者使用传统的循环 ( 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确保函数将始终返回一个承诺,无论其完成,如果函数在没有明确返回值的情况下执行,承诺将被解析undefined,如果它返回某个值,承诺将解析为它,最后,如果函数中的某些东西抛出,承诺将被拒绝。


正如您所看到的,问题在于承诺没有被任何东西等待,而且它们也没有解决任何价值。放置在回调中的 await 实际上对值没有任何作用,就像上面我正在做的.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值,传递三个参数,当前元素,当前索引,再次是数组实例,但是我们明白了。


如果我们想实现一个async 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 相同的顺序获得结果。

但是这个例子和第一个的区别是这个只需要300ms(最长的promise解析),第一个需要600ms(300ms + 200ms + 100ms)。

希望它使它更清楚。


查看完整回答
反对 回复 2021-06-03
  • 1 回答
  • 0 关注
  • 365 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信