在Nodejs/Express中链接Promise的最佳实践

6
我知道在Node.js/Express中链接Promise的最佳方法是这样的:
doSomeThing()
.then()
.then()
.catch();

最近我不得不使用async和q模块来迭代一个列表/数组并运行异步函数。我想知道有没有更好的方法来做/写这个 -

var deferred = Q.defer();
var deferred2 = Q.defer();          
models.Local.findOne({
        where: {
            id: parseInt(req.body.localid)
        }
    })
    .then(function(resultLocal){
        if(!resultLocal){
            return res.status(404).json(
                {
                    "status" : "error",
                    'error': "Local Not Found"
                });
        }
        return models.Documents.create(req.body.document);
    })
    .then(function(docCreated){
            var attributes = req.body.document.Attributes;
            async.each(attributes, function(item, callback) {
                models.Doc_Tags.create({
                    value: item.value,
                    attribute_id: item.id,
                    document_id: docCreated.id
                })
                .then(function(attributeCreated){
                    var upObj = {};
                    upObj[item.col_name] = item.value;

                    models[item.table_name].update(upObj,{
                        where:{
                            id: req.body.document.local_id
                        }
                    })
                    .then(function(primaryUpdated){
                        deferred2.resolve();
                    })
                    .catch(function(error){
                        return res.status(400).json({status: 'error', error:error.message});
                    });

                    deferred2.promise
                    .then(function(){
                        callback();
                    })
                    .catch(function(error){
                        return res.status(400).json({status: "error", error: error.message});
                    });

                })
                .catch(function(error){
                    return res.status(400).json({status: 'error', error:error.message});
                });
            }, function(err,r){
                if( err ) {
                    return res.status(400).json({status: 'error', error:err.message});
                } else {
                    console.log('All attributes Associated');
                    deferred.resolve(docCreated);
                }
            });
            deferred.promise.then(function(result, attributes){
                var obj = req.body.Local;
                models.Local.update(obj, {
                    where: {
                        id: result.local_id
                    }
                })
                .then(function(resultUpdate){
                    return res.status(201).json({status: "success", document: result});
                })
                .catch(function(error){
                    return res.status(400).json({status: "error", error: error.message});
                });
            })
            .catch(function(error){
                return res.status(400).json({status: "error", error: error.message});
            });
        })
    .catch(function(error){
        return res.status(400).json({status: "error", error: error.message});
    });

如果我做错了什么,请纠正我。从功能上来说,代码运行正常,但我认为我可以通过重构使其看起来更好、更易读。

谢谢。


不使用 async.js 和 promises 的组合是此处最重要的良好实践。 - Bergi
@AlongkornChetasumon 当 OP 没有使用那个库时,你不能只是在问题中添加 [tag:Bluebird] 标签! - Bergi
请查看延迟反模式以及如何避免它。 - Bergi
1个回答

4
您的代码可以更加简洁和精炼。
基本思路是:
  • 将回调函数转换为Promise,例如bluebird.js的promisify()方法可以实现
  • 将async.each部分重构为Promise.all以并行调用Promise
  • 重新排列.then链
  • Javascript ES6比旧版本更简洁
示例重构版本:
const Promise = require('bluebird')

// CustomError should be separated to another node module
class CustomError {
  constructor(message, code) {
    this.code = code
    this.message = message
  }
}

let docCreated = undefined

function getPromiseParams(item) {
  return Promise.try(() => {
    return models.Doc_Tags.create({
        value: item.value,
        attribute_id: item.id,
        document_id: docCreated.id
    })
  }).then(attributeCreated => {
    const upObj = {};
    upObj[item.col_name] = item.value;
    return models[item.table_name].update(upObj, { where:{ id: req.body.document.local_id } })
  }).then(primaryUpdated => {
    return docCreated
  }).catch(error => {
    throw new CustomError(error.message, 400)
  })
}

Promise.try(() => {
  return models.Local.findOne({ where: { id: parseInt(req.body.localid) } })
  }).then(resultLocal => {
    if(!resultLocal) throw new CustomError('Local Not Found', 404)

    return models.Documents.create(req.body.document)
  }).then(_docCreated => {
    docCreated = _docCreated // assign value to docCreated

    const attributes = req.body.document.Attributes
    const promiseParams = attributes.map(item => getPromiseParams(item))
    return Promise.all(promiseParams)
  }).then(() => {
    const obj = req.body.Local
    return models.Local.update(obj, { where: { id: result.local_id }})
  }).then(() => {
    return res.status(201).json({status: "success", document: docCreated})
  }).catch(error => {
    return res.status(error.code || 400).json({status: "error", error: error.message});
  })

如果您可以发布重构版本,那将更清晰地指导我如何继续并加深我的理解。您可以跳过繁琐的细节,只提供重构代码的框架即可。谢谢。 - Siddharth Srivastva
@SiddharthSrivastva,看一下上面的例子,我已经更新了我的帖子。 - Alongkorn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接