解决承诺一个接一个(即顺序)?

解决承诺一个接一个(即顺序)?

考虑以下以串行/顺序方式读取文件数组的代码。readFiles返回承诺,只有在所有文件按顺序读取后才能解析。

var readFile = function(file) {
  ... // Returns a promise.};var readFiles = function(files) {
  return new Promise((resolve, reject) => 

    var readSequential = function(index) {
      if (index >= files.length) {
        resolve();
      } else {
        readFile(files[index]).then(function() {
          readSequential(index + 1);
        }).catch(reject);
      }
    };

   readSequential(0); // Start!

  });};

上面的代码可以工作,但我不喜欢为了事情的顺序发生而执行递归。是否有更简单的方法可以重写这段代码,这样我就不用使用我的怪异代码了?readSequential功能?

最初我试着用Promise.all但这导致了readFile调用同时发生,即我想要的是:

var readFiles = function(files) {
  return Promise.all(files.map(function(file) {
    return readFile(file);
  }));};


鸿蒙传说
浏览 423回答 3
3回答

慕工程0101907

如果环境支持异步函数,我将使用它:async function readFiles(files) {   for(const file of files) {     await readFile(file);   }};如果愿意,可以将文件的读取推迟到需要使用异步生成器(如果环境支持的话):async function* readFiles(files) {   for(const file of files) {     yield await readFile(file);   }};更新:重新考虑一下-我可能会使用for循环来代替:var readFiles = function(files) {   var p = Promise.resolve(); // Q() in q   files.forEach(file =>       p = p.then(() => readFile(file));    );   return p;};或更简洁地,减少:var readFiles = function(files) {   return files.reduce((p, file) => {      return p.then(() => readFile(file));   }, Promise.resolve()); // initial};在其他承诺库(如When和Bluebird)中,您有用于此的实用方法。例如,蓝鸟将是:var Promise = require("bluebird");var fs = Promise.promisifyAll(require("fs")); var readAll = Promise.resolve(files).map(fs.readFileAsync,{concurrency: 1 }); // if the order matters, you can use Promise.each instead and omit concurrency paramreadAll.then(function(allFileContents){     // do stuff to read files.});虽然没有任何理由不今天等待使用异步。

波斯汪

下面是我喜欢以串联方式运行任务的方式。function runSerial() {     var that = this;     // task1 is a function that returns a promise (and immediately starts executing)     // task2 is a function that returns a promise (and immediately starts executing)     return Promise.resolve()         .then(function() {             return that.task1();         })         .then(function() {             return that.task2();         })         .then(function() {             console.log(" ---- done ----");         });}有更多任务的案子呢?比如,10?function runSerial(tasks) {   var result = Promise.resolve();   tasks.forEach(task => {     result = result.then(() => task());   });   return result;}

LEATH

这个问题很古老,但是我们生活在一个ES6和函数式JavaScript的世界里,所以让我们看看如何改进。因为承诺会立即执行,我们不能仅仅创建一系列的承诺,它们都会被并行触发。相反,我们需要创建一个返回承诺的函数数组。然后,每个函数将依次执行,然后在内部启动承诺。我们可以用几种方法解决这个问题,但我最喜欢的方法是使用reduce.它的使用变得有点棘手reduce结合承诺,所以我已经把一个班轮分解成一些较小的可消化的咬在下面。这个功能的本质是使用reduce起始值为Promise.resolve([])或包含空数组的承诺。然后,这个承诺将传递给reduce方法ASpromise..这是将每个承诺依次链接在一起的关键。下一个要执行的承诺是func当then触发,将结果连接起来,然后返回该承诺,执行reduce循环下一个承诺函数。一旦所有承诺都已执行,返回的承诺将包含每个承诺的所有结果的数组。ES6示例(一个班轮)/*  * serial executes Promises sequentially.  * @param {funcs} An array of funcs that return promises.  * @example  * const urls = ['/url1', '/url2', '/url3']  * serial(urls.map(url => () => $.ajax(url)))  *     .then(console.log.bind(console))  */const serial = funcs =>     funcs.reduce((promise, func) =>         promise.then(result => func().then(Array.prototype.concat.bind(result))), Promise.resolve([]))ES6示例(细分)// broken down to for easier understandingconst concat = list => Array.prototype.concat.bind(list)const promiseConcat = f => x  => f().then(concat(x))const promiseReduce = (acc, x) => acc.then(promiseConcat(x))/*  * serial executes Promises sequentially.  * @param {funcs} An array of funcs that return promises.  * @example  * const urls = ['/url1', '/url2', '/url3']  * serial(urls.map(url => () => $.ajax(url)))  *     .then(console.log.bind(console))  */const serial = funcs => funcs.reduce(promiseReduce, Promise.resolve([]))用法:// first take your workconst urls = ['/url1', '/url2', '/url3', '/url4'] // next convert each item to a function that returns a promiseconst funcs = urls.map(url => () => $.ajax(url)) // execute them seriallyserial(funcs)     .then(console.log.bind(console))
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript