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

标签 javascript node.js promise fs

<分区>

在下面的代码中,我希望 mark-1mark-2 之前触发。我猜我在某些地方没有正确使用 awaitasync

我很确定这一行:

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

还有这一行

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

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

到目前为止,我还不关心错误检查或跨平台代码,只是让 promise 正常工作

// 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 调用中使用了 asyncawait,这并没有像您预期的那样工作。

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

您应该将 things 数组映射到 promises,并使用 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 ,一个接一个,你可以“减少” promise 数组,或者使用传统的循环(forfor-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 将解析它,最后如果函数中的某些东西抛出,promise 将被拒绝。

如您所见,问题在于 promise 没有被任何东西等待,而且它们也没有解析为任何值。放置在回调中的 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)。

希望它更清楚。

关于javascript - Promise then() 方法没有按预期触发?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56177761/

相关文章:

javascript - 使用 jQuery .promise() 调用 clearInterval()

Javascript:在输入的特定偏移位置附加字符串

javascript - 在 Atom 包中获取整个树守护者解析树

node.js - Chai - 应该有属性(property) - super 测试

node.js - 访问文件夹内的文件,该文件夹位于 sails js(node js 框架)的 config 文件夹内

javascript - 如何处理 $.when promise 的失败?

javascript - history.pushState 如何设置浏览器显示条目的标题?

javascript - 未捕获的 IndexSizeError : ChartJS

javascript - 如何测试询问者验证

node.js - Iced CoffeeScript 是否支持或将要支持 promises?