问题
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