我是 JsonRestStores 的作者。我已经把这个问题推迟得太久了。这是一个将赢得“年度最愚蠢缩进函数”奖的函数。
主要的棘手问题是闭包变量在某个点发生了更改:
body[ self.idProperty ] = params[ self.idProperty ];
还有一个“如果”让事情变得有趣。
那么...有没有一种优雅的方法可以将此函数变成看起来不像带有两个戳的箭头的东西?如果是这样,您能提供一个示例实现吗?
_makePostAppend: function( params, body, options, next ){
var self = this;
var body;
if( typeof( next ) !== 'function' ) next = function(){};
// Check that the method is implemented
if( ! self.handlePostAppend ){
self._sendError( next, new self.NotImplementedError( ) );
return;
}
// Check the IDs
self._checkParamIds( params, body, false, function( err ){
self._sendErrorOnErr( err, next, function(){
self.schema.validate( body, function( err, body, errors ) {
self._sendErrorOnErr( err, next, function(){
if( errors.length ){
self._sendError( next, new self.UnprocessableEntityError( { errors: errors } ) );
} else {
// Fetch the doc
self.execAllDbFetch( params, body, options, function( err, fullDoc ){
self._sendErrorOnErr( err, next, function(){
self.extrapolateDoc( params, body, options, fullDoc, function( err, doc) {
self._sendErrorOnErr( err, next, function(){
self._castDoc( doc, function( err, doc) {
self._sendErrorOnErr( err, next, function(){
// Actually check permissions
self.checkPermissionsPostAppend( params, body, options, doc, fullDoc, function( err, granted ){
self._sendErrorOnErr( err, next, function(){
if( ! granted ){
self._sendError( next, new self.ForbiddenError() );
} else {
// Clean up body from things that are not to be submitted
//if( self.schema ) self.schema.cleanup( body, 'doNotSave' );
self.schema.cleanup( body, 'doNotSave' );
// Paranoid check
// Make sure that the id property in the body does match
// the one passed as last parameter in the list of IDs
body[ self.idProperty ] = params[ self.idProperty ];
self.execPostDbAppend( params, body, options, doc, fullDoc, function( err, fullDocAfter ){
self._sendErrorOnErr( err, next, function(){
self.extrapolateDoc( params, body, options, fullDocAfter, function( err, doc) {
self._sendErrorOnErr( err, next, function(){
self._castDoc( fullDocAfter, function( err, docAfter) {
self._sendErrorOnErr( err, next, function(){
// Remote request: set headers, and send the doc back (if echo is on)
if( self.remote ){
if( self.echoAfterPostAppend ){
self.prepareBeforeSend( docAfter, function( err, docAfter ){
self._sendErrorOnErr( err, next, function(){
self.afterPostAppend( params, body, options, doc, fullDoc, docAfter, fullDocAfter, function( err ){
self._sendErrorOnErr( err, next, function(){
self._res.json( 200, docAfter );
});
});
})
})
} else {
self.afterPostAppend( params, body, options, doc, fullDoc, docAfter, fullDocAfter, function( err ){
self._sendErrorOnErr( err, next, function(){
self._res.send( 204, '' );
});
});
}
// Local request: simply return the doc to the asking function
} else {
self.prepareBeforeSend( docAfter, function( err, docAfter ){
self._sendErrorOnErr( err, next, function(){
self.afterPostAppend( params, body, options, doc, fullDoc, docAfter, fullDocAfter, function( err ){
self._sendErrorOnErr( err, next, function(){
next( null, docAfter, self.idProperty );
})
})
})
})
}
})
});
});
})
}) // err
}) // execPostDbAppend
} // granted
})
})
})
})
})
})
}) // err
}) // checkPermissionsPostAppend
} // errors.length
}) // err
}) // self.validate
}) // err
}) // self.validate
},
最佳答案
如果我编写像您这样的代码,我更愿意使用 async图书馆,它是 waterfall函数,这样我就不必用它的 Promise 版本来包装异步 API。这非常简单。 promise 也很棒,@Esailija 的答案没有任何问题,但我个人认为这更容易实现,并且同样可读:
var async = require('async');
var _makePostAppend = function (params, body, options, next) {
var self = this, body;
if (typeof(next) !== 'function') next = function () { };
// Check that the method is implemented
if (!self.handlePostAppend) {
self._sendError(next, new self.NotImplementedError());
return;
}
async.waterfall([
function (cb) {
// Check the IDs
self.checkParamIds(params, body, false, cb);
},
function (cb) {
self.schema.validate(body, cb);
},
function (body, errors, cb) {
if (errors.length) cb(new self.UnprocessableEntityError({ errors: errors }));
// Fetch the doc
self.execAllDbFetch(params, body, options, cb);
},
function (fullDoc, cb) {
self.extrapolateDoc(params, body, options, fullDoc, function (err, doc) {
cb(err, fullDoc, doc);
});
},
function (fullDoc, doc, cb) {
self._castDoc(doc, function (err, doc) {
cb(err, fullDoc, doc);
});
},
function (fullDoc, doc, cb) {
// Actually check permissions
self.checkPermissionsPostAppend(params, body, options, doc, fullDoc, function (err, granted) {
cb(err, fullDoc, doc, granted);
});
},
function (fullDoc, doc, granted, cb) {
if (!granted) cb(new self.ForbiddenError());
// Clean up body from things that are not to be submitted
//if( self.schema ) self.schema.cleanup( body, 'doNotSave' );
self.schema.cleanup(body, 'doNotSave');
// Paranoid check
// Make sure that the id property in the body does match
// the one passed as last parameter in the list of IDs
body[self.idProperty] = params[self.idProperty];
self.execPostDbAppend(params, body, options, doc, fullDoc, function (err, fullDocAfter) {
cb(err, fullDoc, fullDocAfter);
});
},
function (fullDoc, fullDocAfter, cb) {
self.extrapolateDoc(params, body, options, fullDocAfter, function (err, doc) {
cb(err, fullDoc, doc, fullDocAfter);
});
},
function (fullDoc, doc, fullDocAfter, cb) {
self._castDoc(fullDocAfter, function (err, docAfter) {
cb(err, fullDoc, doc, fullDocAfter, docAfter);
});
}
], function (err, fullDoc, doc, fullDocAfter, docAfter) {
self._sendErrorOnErr(err, next, function () {
// Remote request: set headers, and send the doc back (if echo is on)
if (self.remote) {
if (self.echoAfterPostAppend) {
async.waterfall([
function (cb) {
self.prepareBeforeSend(docAfter, cb);
},
function (docAfter, cb) {
self.afterPostAppend(params, body, options, doc, fullDoc, docAfter, fullDocAfter, cb)
}
], function (err, docAfter) {
self._sendErrorOnErr(err, next, function () {
self._res.json(200, docAfter);
});
});
} else {
self.afterPostAppend(params, body, options, doc, fullDoc, docAfter, fullDocAfter, function (err) {
self._sendErrorOnErr(err, next, function () {
self._res.send(204, '');
});
});
}
// Local request: simply return the doc to the asking function
} else {
async.waterfall([
function (cb) {
self.prepareBeforeSend(docAfter, function (err, docAfter) {
cb(err, doc, fullDoc, fullDocAfter, docAfter);
})
},
function (doc, fullDoc, fullDocAfter, docAfter, cb) {
self.afterPostAppend(params, body, options, doc, fullDoc, docAfter, fullDocAfter, function (err) {
cb(err, docAfter);
});
}
], function (err, docAfter) {
self._sendErrorOnErr(err, next, function () {
next(null, docAfter, self.idProperty);
});
});
}
});
});
};
或者更好的是,我使用了 @Esailija 的答案中的范围界定技巧:
var async = require('async');
var _makePostAppend = function (params, body, options, next) {
var _self = this, _body, _fullDoc, _doc, _docAfter, _fullDocAfter;
if (typeof(next) !== 'function') next = function () { };
// Check that the method is implemented
if (!_self.handlePostAppend) {
_self._sendError(next, new _self.NotImplementedError());
return;
}
async.waterfall([
function (cb) {
// Check the IDs
_self.checkParamIds(params, _body, false, cb);
},
function (cb) {
_self.schema.validate(_body, cb);
},
function (body, errors, cb) {
if (errors.length) cb(new _self.UnprocessableEntityError({ errors: errors }));
// Fetch the doc
_self.execAllDbFetch(params, body, options, cb);
},
function (fullDoc, cb) {
_fullDoc = fullDoc;
_self.extrapolateDoc(params, _body, options, fullDoc, db);
},
function (doc, cb) {
_self._castDoc(doc, cb);
},
function (doc, cb) {
_doc = doc;
// Actually check permissions
_self.checkPermissionsPostAppend(params, _body, options, doc, _fullDoc, cb);
},
function (granted, cb) {
if (!granted) cb(new _self.ForbiddenError());
// Clean up body from things that are not to be submitted
//if( self.schema ) self.schema.cleanup( body, 'doNotSave' );
_self.schema.cleanup(_body, 'doNotSave');
// Paranoid check
// Make sure that the id property in the body does match
// the one passed as last parameter in the list of IDs
_body[_self.idProperty] = params[_self.idProperty];
_self.execPostDbAppend(params, _body, options, _doc, _fullDoc, cb);
},
function (fullDocAfter, cb) {
_fullDocAfter = fullDocAfter;
_self.extrapolateDoc(params, _body, options, fullDocAfter, cb);
},
function (doc, cb) {
_doc = doc;
_self._castDoc(_fullDocAfter, cb);
}
], function (err, docAfter) {
_self._sendErrorOnErr(err, next, function () {
// Remote request: set headers, and send the doc back (if echo is on)
if (_self.remote) {
if (_self.echoAfterPostAppend) {
async.waterfall([
function (cb) {
_self.prepareBeforeSend(docAfter, cb);
},
function (docAfter, cb) {
_self.afterPostAppend(params, _body, options, _doc, _fullDoc, docAfter, _fullDocAfter, cb)
},
function (cb) {
_self._res.json(200, docAfter);
cb();
}
], function (err, results) {
_self._sendErrorOnErr(err, next);
});
} else {
_self.afterPostAppend(params, _body, options, _doc, _fullDoc, docAfter, _fullDocAfter, function (err) {
_self._sendErrorOnErr(err, next, function () {
_self._res.send(204, '');
});
});
}
// Local request: simply return the doc to the asking function
} else {
async.waterfall([
function (cb) {
_self.prepareBeforeSend(docAfter, cb);
},
function (docAfter, cb) {
_docAfter = docAfter;
_self.afterPostAppend(params, _body, options, _doc, _fullDoc, docAfter, _fullDocAfter, cb);
}
], function (err) {
_self._sendErrorOnErr(err, next, function () {
next(null, _docAfter, _self.idProperty);
});
});
}
});
});
};
关于javascript - 扁平化_very_嵌套密集型函数: how to?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19874967/