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

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

问题描述:

在我的节点应用程序中,我需要产生多个文件写入并等待它们完成,然后再继续处理其他内容。我已经通过以下方式实现了这一点: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(); 

虽然它按需运作,但我觉得可能有一些更好的习语。有什么建议么?

+9

承诺和'Promise.all' – zerkms

+2

常量writeFileP =需要( '蓝鸟')。promisify(FS。 writeFile); // writeFileP(...)返回一个承诺 – brainjam

尽管它按照我们的期望工作,但我觉得可能有一些更好的习语。

这是承诺的目的之一。下面是与承诺改写代码(故能走的更远,还有库诺 - IFY的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

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

@Benjamin:再次感谢您!但'Array.from'是必须的,因为我们需要在迭代器上调用'map'(我们没有直接将它传递给'Promise.all')。 –

使用异步:

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的非常知名的库。

以下是您可以对您的案例进行的操作。下面是它会采用并行处理是什么样的一个基本方式:

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

什么是反对票?任何原因? – rclai

+0

是的,你建议不需要理由的外部库。 Promise很好地解决了这个问题,并且内置了它。这是一个写得很好的答案,我只是不相信它适用于JS中的异步操作的当前状态。 –

+0

OP没有说他不想使用外部库。 – rclai

我已经尝试了承诺,我不完全相信,有多大的优势,利用他们在做这样的事情:

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

最后我还是得到一个函数调用,而不是继续在同一个范围内的代码流。 –

+0

@Vadim,当然,但它不一定是一个单独的功能。我添加了一个替代品。实际上,done()与Promise.all(ops).then()几乎相同,除非不必管理promise对象。 – Octopus