最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How do I chain multiple promises? - Stack Overflow

programmeradmin2浏览0评论

I'm not quite sure, and maybe I'm missing something obvious, but I cannot figure out how to chain two promises.

My callback-based code looks something like this:

async.series([
function (cb) {
  // Create the directory if the nodir switch isn't on
  if (!nodir) {
    fs.mkdir('somedirectory', function (err) {
      if (err) {
        log('error while trying to create the directory:\n\t %s', err)
        process.exit(0)
      }
      log('successfully created directory at %s/somedirectory', process.cwd())
      cb(null)
    })
  }
  cb(null)
},
function (cb) {
  // Get the contents of the sample YML
  fs.readFile(path.dirname(__dirname) + '/util/sample_config.yml', function (err, c) {
    var sampleContent = c
    if (err) {
      log('error while reading the sample file:\n\t %s', err)
      process.exit(0)
    }
    log('pulled sample content...')
    // Write the config file
    fs.writeFile('db/config.yml', sampleContent, function (err) {
      if (err) {
        log('error writing config file:\n\t %s', err)
        process.exit(0)
      }
      log('successfully wrote file to %s/db/config.yml', process.cwd())
      cb(null)
    })
  })
}
])

I've begun to try and refactor this to promise-based flow, this is what I have so far:

if (!nodir) {
fs.mkdirAsync('somedirectory').then(function () {
  log('successfully created directory at %s/somedirectory', process.cwd())
}).then(function () {
  // how can I put fs.readFileAsync here? where does it fit?
}).catch(function (err) {
  log('error while trying to create the directory:\n\t %s', err)
  process.exit(0)
})
}

(I'm using Bluebird, so I did Promise.promisifyAll(fs) before)

The problem is, I have no idea where to put the second "step" of the previous series. Do I put it in then or in its function? Do I return it and put it in a separate function?

Any help would greatly be appreciated.

I'm not quite sure, and maybe I'm missing something obvious, but I cannot figure out how to chain two promises.

My callback-based code looks something like this:

async.series([
function (cb) {
  // Create the directory if the nodir switch isn't on
  if (!nodir) {
    fs.mkdir('somedirectory', function (err) {
      if (err) {
        log('error while trying to create the directory:\n\t %s', err)
        process.exit(0)
      }
      log('successfully created directory at %s/somedirectory', process.cwd())
      cb(null)
    })
  }
  cb(null)
},
function (cb) {
  // Get the contents of the sample YML
  fs.readFile(path.dirname(__dirname) + '/util/sample_config.yml', function (err, c) {
    var sampleContent = c
    if (err) {
      log('error while reading the sample file:\n\t %s', err)
      process.exit(0)
    }
    log('pulled sample content...')
    // Write the config file
    fs.writeFile('db/config.yml', sampleContent, function (err) {
      if (err) {
        log('error writing config file:\n\t %s', err)
        process.exit(0)
      }
      log('successfully wrote file to %s/db/config.yml', process.cwd())
      cb(null)
    })
  })
}
])

I've begun to try and refactor this to promise-based flow, this is what I have so far:

if (!nodir) {
fs.mkdirAsync('somedirectory').then(function () {
  log('successfully created directory at %s/somedirectory', process.cwd())
}).then(function () {
  // how can I put fs.readFileAsync here? where does it fit?
}).catch(function (err) {
  log('error while trying to create the directory:\n\t %s', err)
  process.exit(0)
})
}

(I'm using Bluebird, so I did Promise.promisifyAll(fs) before)

The problem is, I have no idea where to put the second "step" of the previous series. Do I put it in then or in its function? Do I return it and put it in a separate function?

Any help would greatly be appreciated.

Share Improve this question asked May 22, 2015 at 19:55 jonajona 3994 silver badges14 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 5

Normally promise-driven code looks like this:

operation.then(function(result) {
  return nextOperation();
}).then(function(nextResult) {
  return finalOperation();
}).then(function(finalResult) {
})

There's a lot going on in your example, but the general idea would be something like:

Promise.resolve().then(function() {
  if (nodir) {
    return fs.mkdir('somedirectory').catch(function(err) {
      log('error while trying to create the directory:\n\t %s', err);
      process.exit(0);
    });
  }
}).then(function() {
  return fs.readFile(path.dirname(__dirname) + '/util/sample_config.yml').catch(function(err) {
    log('error while reading the sample file:\n\t %s', err);
    process.exit(0);
  })
}).then(function(sampleContent) {
  log('pulled sample content...');

  return fs.writeFile('db/config.yml', sampleContent).catch(function(err) {
    log('error writing config file:\n\t %s', err)
    process.exit(0)
  })
}).then(function() {
  log('successfully wrote file to %s/db/config.yml', process.cwd())
})

This presumes all the calls you're using are promise native.

The original Promise.resolve() is simply something to start out the chain with a promise since your first step is conditional.

Since you're using Bluebird, you can use a lot of sugar and have much much cleaner code than the accepted answer IMO:

var fs = Promise.promisifyAll(fs); // tell bluebird to work with FS
Promise.try(function(){ 
  if(nodir) return fs.mkdirAsync('somedirectory').
                      catch(catchErr("Could not create dir"));
}).then(function(){
    return fs.readFileAsync(path.dirname(__dirname) + '/util/sample_config.yml').
             catch(catchErr("error while reading the sample file"));
}).then(function(data){
  log('pulled sample content...');
  return fs.writeFile('db/config.yml', data).
            catch(catchErr("error writing config file"));
}).then(function(){
  log('successfully wrote file to %s/db/config.yml', process.cwd())
}, function(err){
    // centralized error handling, to remove the redundancy
    log(err.message);
    log(err.internal);
    log(err.stack); // this is important! 
});


function catchErr(msg){ // helper to rethrow with a specific message
    return function(e){
        var err = new Error(msg);
        err.internal = e; // wrap the error;
        throw e;
    };
}

I'd go further though, and would remove the finer grained errors you have here since the types you use really provide no additional info over the built in messages these API methods provide - shortening your code to:

Promise.try(function(){
    if(nodir) return fs.mkdirAsync("somedirectory");
}).then(function(){
    fs.readFileAync(path.dirname(__dirname) + '/util/sample_config.yml');
}).then(function(data){
    log('pulled sample content...');
    return fs.writeFile('db/config.yml', data);
}).then(function(){
  log('successfully wrote file to %s/db/config.yml', process.cwd())
}).catch(function(err){
    log(err);
    process.exit(1);
});

Promises are throw safe and provide sane and certralized error handling - pretty nice win if you ask me. It gets better though, if you're on io.js or modern node you can use:

Promise.coroutine(function*(){ // generators ftw
  if(nodir) yield fs.mkdirAsync("somedirectory");
  var data = yield fs.readFileAsync(path.dirname(__dirname) + '/util/sample_config.yml');
  log("pulled sample content");
  yield fs.writeFileAsync("db/config.yml", data);
  log('successfully wrote file to %s/db/config.yml', process.cwd());
})();

process.on("unhandledRejection", function(p, r){
    throw r; // quit process on promise failing
});
发布评论

评论列表(0)

  1. 暂无评论