【问题标题】:Look for Promise bluebird code review for node.js寻找 node.js 的 Promise bluebird 代码审查
【发布时间】:2026-02-08 15:05:02
【问题描述】:

何时何地需要使用new Promise(Function<Function resolve, Function reject> resolver) -> Promise

我的示例代码:

userInfo.js

var Promise = require('bluebird');
var winston = require('winston');
var _ = require('lodash');
var request = Promise.promisify(require("request"));

exports.getWeather = function (data) {
    var cityName = data.userProfile.city;
    return request("http://0.0.0.0:3003/api/Weather/byCity?city=" + cityName).spread(function (res, body) {
        var result = JSON.parse(body).data;
        return _.merge(data, result);
    });
};

exports.getUserProfile = function (userId) {
    return new Promise(function (resolve, reject) {
        request("http://0.0.0.0:3003/api/UserProfile/getUserProfile?id=" + userId).spread(function (res, body) {
            var result = JSON.parse(body).data;
            resolve(result);
        });
    })

};

exports.getEvents = function (data) {
    var cityName = data.userProfile.city;
    return request("http://0.0.0.0:3003/api/Events/byCity?city=" + cityName).spread(function (res, body) {
        var result = JSON.parse(body).data;
        return _.merge(data, result);
    });
};

exports.getFashion = function (data) {
    var gender = data.userProfile.gender;
    return request("http://0.0.0.0:3003/api/Fashion/byGender?gender=" + gender).spread(function (res, body) {
        var result = JSON.parse(body).data;
        return _.merge(data, result);
    });
};

exports.displayDetail = function (data) {
    console.log(data);
};

上面的代码我尝试在 promise 中以 2 方式调用

getUserProfile.js

var userInfo = require('./userInfo');
    module.exports = function(){
       return userInfo.getUserProfile(3)
                    .then(userInfo.getFashion)
                    .then(userInfo.getEvents)
                    .then(userInfo.getWeather)
                    .then(userInfo.displayDetail)
                    .catch(function (e) {
                        console.log('Error:');
                        console.error(e.stack)
                    })
                    .finally(function () {
                        console.log('done');
                    });

    }

第二种方式:

getUserInformation.js

var userInfo = require('./userInfo');
     module.exports = function () {
        return new Promise(function (resolve, reject) {

             resolve(3);
        })
            .then(userInfo.getUserProfile)
                .then(userInfo.getFashion)
                .then(userInfo.getEvents)
                .then(userInfo.getWeather)
                .then(userInfo.displayDetail)
                .catch(function (e) {
                    console.log('Error:');
                    console.error(e.stack)
                })
                .finally(function () {
                    console.log('done');
                });
    };

getDetails.js

var userInfo = require('./getUserInformation');
    userInfo()
    .then(function(){
            console.log('getDetails done')
        })
        .catch(function (e) {
            console.log('Error:');
            console.error(e.stack)
        })
        .finally(function () {
            console.log('done');
        });

请告诉我有什么区别,使用这些方式有什么问题吗?

【问题讨论】:

  • 有什么区别?什么样的问题?
  • 我正在寻找代码审查..哪种方法是正确的?
  • 两者都是,假设另一种方法是使用 node.js 样式回调。
  • 但是创建虚拟的新 Promise 然后进行链接是对的吗?对了,后台发生了什么……是否存在任何性能问题?
  • 当你使用的东西没有实现 promise api。

标签: javascript node.js promise bluebird


【解决方案1】:
exports.getUserProfile = function (userId) {
    return new Promise(function (resolve, reject) {
        request("http://0.0.0.0:3003/api/UserProfile/getUserProfile?id=" + userId).spread(function (res, body) {
            var result = JSON.parse(body).data;
            resolve(result);
        });
    })
};

Please don't do this。只需来自回调的return,并返回由then 创建的承诺,就像您在其他三个方法中所做的那样。

return userInfo.getUserProfile(3)
.then(…)

对比

return new Promise(function (resolve, reject) {
    resolve(3);
})
.then(userInfo.getUserProfile)
.then(…)

嗯,第一个更具可读性和简洁性。除了getUserProfile 确实同步抛出的情况外,它们几乎是等价的,无论如何都不应该这样。同样在第一种情况下,getUserProfile 作为方法调用onuserInfo,而在第二种情况下,它只是一个回调函数,调用中的this 会有所不同。

通过使用Promise.resolve 而不是new Promise 构造函数,可以极大地简化第二种模式:

return Promise.resolve(3)
.then(userInfo.getUserProfile)
.then(…)

这完全没问题,并且与链条的其余部分更好地对齐。说起来,……

.then(userInfo.getFashion)
.then(userInfo.getEvents)
.then(userInfo.getWeather)

每个函数都返回一个promise,该promise 解析为 附加数据合并到它的参数中

并不是解决这个问题的最佳方法。是的,它确保这三个函数被一个接一个地调用,并且is an acceptable pattern for that case。但是,在您的情况下,您将对 API 的 request 调用与该参数提取和结果合并混合在同一个函数中;通过分离关注点,你不应该这样做。而是让函数纯粹

exports.… = function (arg) {
    return request("http://0.0.0.0:3003/api/…?…=" + arg).spread(function (res, body) {
        return JSON.parse(body).data;
    });
};

现在您可以单独组合它们 - 不仅可以按顺序组合,还可以并行组合:

userInfo.getUserProfile(3)
.then(function(data) {
    var p = data.userProfile;
    return Promise.prop({
         userProfile: 0,
         fashion: userInfo.getFashion(p.gender), // `\
         events: userInfo.getEvents(p.city),     //   }=> execute requests in parallel
         weather: userInfo.getWeather(p.city)    // ./
    });
})
.then(userInfo.displayDetail)
.catch(function (e) {
     console.error('Error:', e.stack)
});

【讨论】:

    【解决方案2】:

    第一种方式更具可读性,并且像第二种方式一样,使用返回常量的 Promise 启动链没有任何好处。

    他们都有效地做同样的事情,有一个警告:在你的第二个例子中(用 Promise 启动链),getUserProfile 调用将在下一个滴答时运行(类似于你把它扔进去一个 setTimeout 0) 而不是原子的。

    【讨论】: