Javascript JS:等待回调完成的优雅方式

Javascript JS:等待回调完成的优雅方式,javascript,node.js,Javascript,Node.js,在我的节点应用程序中,我需要生成多个文件写入,并等待它们完成,然后再进行其他操作。我通过以下方式实现了这一点: let counter = 0; (some loop declaration) { // (preparing data etc) counter += 1; fs.writeFile(fname, fdata, (err) => { counter -= 1; }); } let waitForCallbacks = funct

在我的节点应用程序中,我需要生成多个文件写入,并等待它们完成,然后再进行其他操作。我通过以下方式实现了这一点:

let counter = 0;
(some loop declaration) {
    // (preparing data etc)
    counter += 1;
    fs.writeFile(fname, fdata, (err) => {
        counter -= 1;
    });
}
let waitForCallbacks = function() {
    if (fcounter > 0) {
        setTimeout(waitForCallbacks, 0);
    }
};
waitForCallbacks();
虽然它能按预期工作,但我觉得可能会有更好的习惯用法。有什么建议吗

虽然它能按预期工作,但我觉得可能会有更好的习惯用法

这是承诺的目的之一。下面是用承诺重写的代码(它可以更进一步,有libs来承诺ifynodejsapi):

或考虑如果我们有一个允诺版本的<代码>写文件< /代码>:

let operations = []
(some loop declaration) {
    // (preparing data etc)
    operations.push(writeFileWithPromise(fname, fdata));
}
Promise.all(operations).then(() => {
    // All done
});
或者,如果“循环”在一个iterable上,我们可以将其转换为一个数组,并在上使用
map

Promise.all(
    Array.from(theThingy).map(entry => writeFileWithPromise(entry.fname, entry.fdata))
).then(() => {
    // All done
});
使用异步:

async.map([['name1', 'data1'], ['name2', 'data2'], ['name3', 'data3']], 
function (item, cb) {
     fs.writeFile(item[0], item[1], (err, res) => {
        callback(err, res);
    });
}, 
function(err, results) {
    // results is now an array of results
});

承诺是一种方式。然而,如果你不喜欢它们,你可以使用一个非常有名的图书馆,叫做

这是你能为你的案子做的。下面是使用并行处理的基本方式:

async.parallel([
  function(done) {
    fs.writeFile(fname1, fdata1, (err) => {
      done(err, customResults1);
    });
  },
  function(done) {
    fs.writeFile(fname2, fdata2, (err) => {
      done(err, customResults2);
    });
  }
],
// The callback when every function above is done
function(err, results) {
  // `results` contains a collection of what you've passed
  // on the `done` callbacks above
});
为了更具体地针对您的用例,它将如下所示:

async.parallel(
  ['file1', 'file2'].map(function(fname) {
    return function(done) {
      fs.writeFile(fname, fdata, (err) => {
        done(err, customResults);
      });
    };
  }),
  // The callback when every function above is done
  function(err, results) {
    // `results` contains a collection of what you've passed
    // on the `done` callbacks above
  }
);

如果希望任务按使用顺序逐个运行。如果希望函数将其结果传递给下一个任务,可以使用。当然,您还可以使用其他功能,但我提到的是一些非常常见的功能。

我曾尝试过承诺,但我并不完全相信使用它们比执行类似操作有多大优势:

    let counter = 0;
    (some loop declaration) {
            // (preparing data etc)
            counter += 1;
            fs.writeFile(fname, fdata, (err) => {
                    counter -= 1;
                    if ( counter==0 ) done()
            });
    }

    function done(){
        // that's it 
    }
与初始代码非常类似,除了不使用setTimeout()之外,我在每个操作完成时检查计数器的值

依我看,这同样有效,也许更容易阅读

或者,如果你不喜欢有一个单独的功能来完成

    let counter = 0;
    (some loop declaration) {
            // (preparing data etc)
            counter += 1;
            fs.writeFile(fname, fdata, (err) => {
                    counter -= 1;
                    if ( counter==0 ) {
                            // that's it, finish up
                    }
            });
    }

promisify(fs.writeFile);//writeFileP(…)返回一个promiseYes,您推荐了一个外部库,但没有必要的原因。承诺很好地解决了这个问题,并且是内在的。这是一个写得很好的答案,但鉴于JS中异步操作的当前状态,我不认为它有用。OP没有说他不想使用外部库。是的,在这种情况下不需要一个。在不需要外部依赖的地方引入外部依赖对我来说似乎是不必要的。caolan/async在几年前是一个重要的库,但现在它大多已成为过去。我认为向新用户推荐它是对他们的伤害。当库仍在积极维护时,它怎么会成为过去呢?它最终还是以一个函数调用结束,而不是在相同的范围内继续代码流。只有一个回调而不是多个回调。我最终还是得到了一个函数调用,没有在同一个范围内继续代码流。@Vadim,当然,但它不必是一个单独的函数。我添加了一个替代方案。事实上,done()实际上与Promise.all(ops.then()相同,只是不需要管理Promise对象。@Benjamin:再次感谢您!但是
数组.from
是必需的,因为我们需要在iterable上调用
映射(我们不会将它直接传递到
Promise.all
)。
    let counter = 0;
    (some loop declaration) {
            // (preparing data etc)
            counter += 1;
            fs.writeFile(fname, fdata, (err) => {
                    counter -= 1;
                    if ( counter==0 ) {
                            // that's it, finish up
                    }
            });
    }