在foreach循环中使用回调函数

时间:2019-03-09 15:35:31

标签: javascript foreach es6-promise

我正在尝试在forEach循环中使用带有回调的函数。

在继续执行下一步之前,我需要等待执行完成。

这是我的代码:

const arr = '6,7,7,8,8,5,3,5,1'

const id = arr.split(',');

const length = id.length;


id.forEach( (x, index) => {
  (function FnWithCallback () {
    setTimeout(() => { console.log(x) }, 5000);
  })();
});

console.log('done');

我想出了一个办法:

const arr = '6,7,7,8,8,5,3,5,1'

const id = arr.split(',');

const length = id.length;

const fn = () => {
  return new Promise (resolve => {
    id.forEach( (id, index) => {
      setTimeout(() => {console.log(id)}, 3000);

      if(index === (length - 1))
         resolve();
    })
  })
}

fn().then(()=> {
  console.log('done');
})

但黑客似乎已被破解。

我可以对此找到真正的解决方案吗? NPM软件包确实很有帮助。

注意:我看过async.js。我不确定这是否是我想要的东西,因为我正努力避免发生回调地狱。

4 个答案:

答案 0 :(得分:5)

解决方案是 promisify 回调函数,然后将Array.prototype.map()Promise.all()结合使用:

const arr = '6,7,7,8,8,5,3,5,1'

function FnWithCallback (id, cb) {
  setTimeout(cb, 1000, id)
}

const promisified = id => new Promise(resolve => {
  FnWithCallback(id, resolve)
})

const promises = arr.split(',').map(promisified)

Promise.all(promises).then(id => {
  console.log(id)
  console.log('Done')
})

如果您的回调API遵循(error, result) => ...的Node.js约定,那么您应该使用util.promisify()来使该函数合理化,或者检查文档以查看是否省略回调参数是否会导致调用之所以返回承诺,是因为许多软件包现在都提供了现成的基于承诺的API。

答案 1 :(得分:4)

如果您要确保异步操作不是并行运行,而是一个接一个地运行,请继续阅读。如果您想按顺序获取结果,但不关心它们是否并行运行,请参见Patrick's answer

您可以使用async函数来在await循环中for做出承诺,因为承诺计时器确实很有用:

const timer = ms => new Promise(res => setTimeout(res, ms));

(async function() {
  for(const id of ["6", "7", "7" /*...*/]) {
     await timer(5000);
     console.log(id);
  }
  console.log("done");
})();

这也可以使用回调链来实现,但是我不确定这是否可以理解/有用(只是想表明回调不必来自地狱):

["6", "7", "7" /*..*/].reduceRight(
  (next, id) => () => setTimeout(() => {
     console.log(id);
     next();
  }, 5000),
  () => console.log("done")
)();

答案 2 :(得分:1)

您可以使用Array.prototype.reduce()链接承诺,从初始已解决的承诺开始。您必须将回调变成承诺,才能链接它们:

const arr = '6,7,7,8,8,5,3,5,1'
const ids = arr.split(',');
const length = ids.length;

const timeout = (fn, ms) => new Promise(res => {
  setTimeout(() => { fn(); res(); }, ms);
});

ids.reduce((previousPromise, id) => {
  return previousPromise.then(() => {
    return timeout(() => console.log(id), 200);
  });
}, Promise.resolve());

console.log('done');

或使用异步/等待:

const arr = '6,7,7,8,8,5,3,5,1'
const ids = arr.split(',');
const length = ids.length;

const timeout = (fn, ms) => new Promise(res => {
  setTimeout(() => { fn(); res(); }, ms);
});

ids.reduce(async (previousPromise, id) => {
  await previousPromise;
  return timeout(() => console.log(id), 200);
}, Promise.resolve());

console.log('done');

答案 3 :(得分:0)

您可以尝试这种方法,其中for i in range(1000): data = pd.read_csv("basename{i}.csv".format(i=i)) //do something with data 将收到任何类型的值作为之后要解析的参数。

promisefy

或使用Array reduce()

const IDs = '6,7,7,8,8,5,3,5,1'.split(',');

// simulates an async response after 1s
const promisefy = (value) => new Promise((resolve) => {
  setTimeout(() => {
    resolve(value);
  }, 1000);
});

// stores all async responses
const responses = IDs.map((id, index) => {
  return promisefy(id);
});

// executes sequentially all responses
responses.forEach((resp, index) => {
  resp.then((value) => console.log(`id: ${value}, index: ${index}`));
});