How to properly chain promises using reduce

醉酒当歌 提交于 2021-01-29 05:15:41

问题


I have a system that wants to create many folders in dropbox using the api, however i appear to attempt to create all the folders at once which causes errors to be generated, stating that i am performing too many write operations from dropbox.

My code is as follows and first uses reduce to create multiple promises which i thought were chained. In these promises the function add is called, which uploads the case to mongodb and then creates a dropbox folder for it, however this results in throttling errors..

bulkAdd: function (req, callback) {
  issues = []
  i = 1

  req.reduce((promise, audit) => {
    return promise.then(_ => this.add(audit, function(err,data){
      if (err){
        console.log('\n'+i+ ' ' + data.scanner_ui + '\n');
      }
    }));
  }, Promise.resolve()).catch(error => {console.log(error)});
},

add: function (req, callback) {
  delete req.status
  var audit = new Audit(req);
  if (req['status_value'] != undefined && req.status_value != ''){
    console.log(req['status_value'])
    audit.status = [{
      status_value : req['status_value'],
      status_notes : req['status_notes'],
      status_date : req['status_date'],
    }]
  }

  audit.save(function (err, data) {
    if (err) {
      callback(err, data)
    }
    else {
        return dropbox_functions.createFolder(data.ui)
          .then(response => {console.log(response)}, error=> {console.log('\n\n\n',error.error.error)})
        .catch(error => console.log(error))
    }
  }); 

},


回答1:


So the problem in your current question comes from the fact that your add function doesn't return a value, I only see it returning undefined.

If a promise returns something else than a promise in its then / catch block, it will use this input for the following function, and it will not wait for any internal processes to run through before finishing

If inside your then / catch blocks, you would return a promise, it would wait before continuing to the next then block, thus handling your requests sequential.

Now in your current code, I believe the easiest would be to handle the resolve inside your reduce, since you seem to be stuck with your callback handle already.

req.reduce((promise, audit) => {
  return promise.then(_ => new Promise( 
    function( resolve, reject) {
      this.add(audit, function(err,data){
        if (err){
          console.log('\n'+i+ ' ' + data.scanner_ui + '\n');
          reject( err );
          return;
        }
        resolve( data );
      });
    })
  );
}, Promise.resolve()).catch(error => {console.log(error)});

in this case, the promise would either reject or resolve itself. I have chosen to submit err and data respectively, so this would be handled in the end in case an error occurs, and so that you can have the last data that got saved successfully




回答2:


To properly chain you should make sure that you return a promise object.

Your reduce in the end creates a promise chain something like this.

Promise.resolve()
    .then(() => {
        console.log('setting up first timeout');
        setTimeout(() => {
            console.log("1 wait");
        }, 2000);
    })
    .then(() => {
      console.log('setting up second timeout');
        setTimeout(() => {
            console.log("2 wait");
        }, 2000);
    })
    .catch(err => console.log("error", err));

If you run it, you'll see that it does not wait for one promise to end before moving down the chain.

Whereas, in the second example it waits for the first one to complete because a Promise object is returned by the first promise.

Promise.resolve()
  .then(() => {
    return new Promise(function(resolve, reject) {
      console.log('setting up first timeout');
      setTimeout(() => {
        console.log("1 wait");
        resolve();
      }, 2000);
    });
  })
  .then(() => {
    return new Promise(function(resolve, reject) {
      console.log('setting up second timeout');
      setTimeout(() => {
        console.log("2 wait");
        resolve();
      }, 2000);
    });
  })
  .catch(err => console.log("error", err));

The difference is the first example is not returning Promise object. So, if you make sure each success handler returns a Promise object which is resolved only after your add function is done executing, you should be fine.




回答3:


Unless there is a good reason for bulkAdd() to be written in nodeback style, you will find it more convenient to return a promise. The need for a nodeback will disappear and .reduce() will sit much more comfortably inside the function.

Using .reduce() and preserving nodeback style is possible but cumbersone, as it involves a rather ugly double-shuffle from nodeback to promise, and promise back to nodeback.

Assuming you are free to adapt the caller(s) to accept a returned promise, the code would be something like this :

'bulkAdd': function(req) {
    return req.reduce((promise, audit) => {
        return promise.then(_ => this.add(audit));
    }, Promise.resolve());
},
'add': function(req) {
    delete req.status;
    var audit = new Audit(req);
    if (req.status_value != undefined && req.status_value != '') {
        audit.status = [{
            'status_value': req.status_value,
            'status_notes': req.status_notes,
            'status_date': req.status_date
        }];
    }
    return new Promise((resolve, reject) => { // in-line promisification of audit.save()
        audit.save((err, data) => {
            err ? reject(err) : resolve(data);
        });
    })
    .then(data => dropbox_functions.createFolder(data.ui));
},

all catching and logging intentionally removed

The detail may differ, depending primarily on :

  • what data (if any) you want to be delivered to the caller
  • what you want to happen when errors occur.


来源:https://stackoverflow.com/questions/53708437/how-to-properly-chain-promises-using-reduce

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!