【问题标题】:Promise then() method is not firing as expected? [duplicate]Promise then() 方法没有按预期触发? [复制]
【发布时间】:2019-10-04 06:36:29
【问题描述】:

在下面的代码中,我希望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');
}

【问题讨论】:

    标签: javascript node.js promise fs


    【解决方案1】:

    问题是您在forEach 调用中使用了asyncawait,这并没有像您预期的那样工作。

    forEach 方法并不真正关心回调函数的返回值(在这种情况下,getStats 返回的承诺)。

    您应该将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);
    }
    

    请注意,这将“并行”执行 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 将用@ 解决987654338@,如果它返回某个值,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 to resolve),而第一个需要600ms(300ms + 200ms + 100ms)。

    希望它更清楚。

    【讨论】:

    • 您是说 forEach 中的 await 不会在此时停止程序吗?
    • @J.M.,对,forEach 方法只会触发回调,返回的 Promise 会丢失。
    • 这违背了 MDN。 await 应该会导致等待... developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
    • ...来自 MDN 文档...“await 表达式导致异步函数执行暂停,直到 Promise 被解决”...
    • 不,它不违背它。只需在传递给 forEach 的回调中关注片刻。 forEach 方法在内部只是执行函数,它不等待它,它实际上根本不关心回调的返回值。
    猜你喜欢
    • 2015-11-08
    • 1970-01-01
    • 2021-08-14
    • 2016-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-21
    • 2020-08-19
    相关资源
    最近更新 更多