Promise then() 方法没有按预期触发? [复制]

Posted

技术标签:

【中文标题】Promise then() 方法没有按预期触发? [复制]【英文标题】:Promise then() method is not firing as expected? [duplicate] 【发布时间】: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');

【问题讨论】:

【参考方案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 方法在内部只是执行函数,它不等待它,它实际上根本不关心回调的返回值。

以上是关于Promise then() 方法没有按预期触发? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

Promise 的三种状态,以及then、catch的链式调用

jQuery Deferred/promise 没有按预期工作

.play() 在 React 组件上无法按预期工作

Promise.prototype.then()学习

.then 在 promise 解决之前触发

执行 .Then() 承诺 NodeJS