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

时间:2016-09-14 22:03:41

标签: javascript node.js

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

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();

虽然它按预期工作,但我觉得可能会有更好的成语。有什么建议吗?

4 个答案:

答案 0 :(得分:4)

  

虽然它按预期工作,但我觉得可能会有更好的成语。

这是承诺的目的之一。这里的代码是用promises重写的(它可以更进一步,有一些libs可以承诺 - 如果是NodeJS API):

let operations = []
(some loop declaration) {
    // (preparing data etc)
    operations.push(new Promise((resolve, reject) => {
        fs.writeFile(fname, fdata, (err) => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    }));
}
Promise.all(operations).then(() => {
    // All done
});

或者考虑我们是否有writeFile的承诺版本:

let operations = []
(some loop declaration) {
    // (preparing data etc)
    operations.push(writeFileWithPromise(fname, fdata));
}
Promise.all(operations).then(() => {
    // All done
});

或者如果"循环"是一个可迭代的,我们可以变成一个数组并使用map on:

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

答案 1 :(得分:2)

承诺是一种方式。但是,如果您不喜欢它们,可以使用名为async的知名库。

以下是您可以对案件采取的措施。以下是使用并行处理的初步方式:

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
  }
);

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

答案 2 :(得分:-1)

使用Async:

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
});

答案 3 :(得分:-1)

我已经尝试过Promises,我并不完全相信使用它们做这样的事情有很多好处:

    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(),我会在每个操作完成时检查计数器的值。

IMO,这同样有效,也许更容易阅读。

或者,如果您不想要单独的功能来完成......

    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
                    }
            });
    }