NodeJS Promises:合并多个HTTP请求的正确方法返回

时间:2016-11-09 20:30:28

标签: javascript node.js api promise httprequest

有一个NodeJS流程可以通过Web服务访问一个名为Kudos的东西。这些荣誉从一个人发送到另一个人/或一群人。我要做的是创建一条包含以下内容的消息:

从{poster}到{receiver / s}的荣誉

{Kudos Message}

目前我的流程正常工作,可以将海报发送到一个接收器。我正在努力使其能够获得接收器的多个名称。

问题源于这样一个事实:它返回接收赞誉的用户的部分,它只提供用户ID。所以我需要再打一次电话来获取用户的名字。我很容易得到承诺,为一个用户工作,但我似乎可以正确地获得多个用户。

包含多个用户的JSON数据如下所示:

 "notes_user": [
  {
    "id": "1060",
    "note_id": "795",
    "user_id": "411"
  },
  {
    "id": "1061",
    "note_id": "795",
    "user_id": "250"
  },
  {
    "id": "1062",
    "note_id": "795",
    "user_id": "321"
  }
],

以下是完成大部分工作的功能:

getMaxId返回当前处理过的最高荣誉的数据库索引,而getKudos只返回" kudos"的json数据集。

function promisifiedKudos() {
var maxid;
var newmaxid;

Promise.all([getMaxId(), getKudos()])
    .then(function(results) {
        maxid = results[0];

        var kudos = results[1];
        newmaxid = kudos[0].id;
        return kudos.filter(function(kudo) {
            if (maxid < kudo.id) {
                return kudo;
            }
        })
    })
    .each(function(kudo) {
        return getTribeUserName(kudo);
    })
    .then(function(results) {
        return results.map(function(kudo) {
            var message = "Kudos from " + kudo.poster.full_name + " to " + kudo.kudo_receiver_full_name + "\r\n";
            message += "\r\n";
            return message += entities.decode(striptags(kudo.note));
        })
    })
    .each(function(message) {
        return postStatus(message);
    })
    .then(function() {
        var tribehr = db.get('tribehr');
        console.log(new Date().toString() + ":Max ID:" + newmaxid);
        tribehr.update({ endpoint: "kudos" }, { $set: { id: newmaxid } });
    })
    .done(function(errors) {
        console.log("Run Complete!");
        return "Done";
    });
}

辅助函数getTribeUserName()

function getTribeUserName(kudo) {
return new Promise(function(fulfill, reject) {
    var id = kudo.notes_user[0].user_id;
    var options = {
        url: "https://APIURL.com/users/" + id + ".json",
        method: "GET",
        headers: {
            "Authorization": "Basic " + new Buffer("AUTHCODE" + AUTHKEY).toString('base64')
        }
    }
    request.getAsync(options).then(function(response) {
        if (response) {
            var data = JSON.parse(response.body)
            kudo.kudo_receiver_full_name = data.User.full_name;
            fulfill(kudo);
        } else {
            reject("Get Tribe User Name Failed");
        }
    });
});
}

我尝试添加一个调用getTribeUserName()的辅助函数,如下所示:

function getTribeUsers(kudo) {
return new Promise(function(fulfill, reject) {
    kudo.notes_user.map(function(user) {
        //Make calls to a getTribeUserName
    })
});
}

但结果是,当最终消息放在一起时,用户名是未定义的。

任何有关如何更好地使用promises的指示都会非常有帮助。这是我第一次与他们一起刺,我希望我朝着正确的方向前进。我知道我需要添加错误检查,但目前我只是想让这个过程适用于多个用户。

2 个答案:

答案 0 :(得分:0)

如果您需要使用作为resolve函数参数传递的promise的结果,那么您可以在then onFulfilled回调中捕获它。

如果您需要将链中的then方法中获得的某些数据传递给另一个then,您只需将其返回并通过以下onFulfilled回调来捕获它{1}}。

then

答案 1 :(得分:0)

如果要异步获取多个TribeUserNames,那么您需要以某种方式汇总多次调用getTribeUserNames()返回的承诺。

你可以写Promise.all(array.map(mapper))但Bluebird提供更方便的Promise.map(array, mapper)

Bluebird的.spread()也很方便,可以引用maxidkudos

这是一个我能管理的简单形式:

function promisifiedKudos() {
    return Promise.all([getMaxId(), getKudos()])
    .spread(function(maxid, kudos) {
        var newmaxid = kudos[0].id;
        // The following line filters (synchronously), adds TribeUserNames (asynchronously), and delivers an array of processed kudos to the next .then().
        return Promise.map(kudos.filter((kudo) => kudo.id > maxid), getTribeUserName)
        .then(function(filteredKudosWithTribeUserNames) { // in practice, shorten arg name to eg `kudos_`
            return Promise.map(filteredKudosWithTribeUserNames, function(kudo) {
                return postStatus("Kudos from " + kudo.poster.full_name + " to " + kudo.kudo_receiver_full_name + "\r\n\r\n" + entities.decode(striptags(kudo.note)));
            });
        })
        .then(function() {
            var tribehr = db.get('tribehr');
            console.log(new Date().toString() + ":Max ID:" + newmaxid);
            return tribehr.update({ endpoint: 'kudos' }, { $set: { 'id': newmaxid } });
        });
    })
    .then(function() {
        console.log('Run Complete!');
    }).catch(function(error) {
        console.log(error);
        throw error;
    });
}

getTribeUserName()需要返回一个承诺,可以写成如下:

function getTribeUserName(kudo) {
    var options = {
        'url': 'https://APIURL.com/users/' + kudo.notes_user[0].user_id + '.json',
        'method': 'GET',
        'headers': {
            'Authorization': 'Basic ' + new Buffer('AUTHCODE' + AUTHKEY).toString('base64')
        }
    }
    return request.getAsync(options).then(function(response) {
//  ^^^^^^
        if(response) {
            kudo.kudo_receiver_full_name = JSON.parse(response.body).User.full_name;
        } else {
            throw new Error(); // to be caught immediately below.
        }
        return kudo;
    }).catch(function(error) { // error resistance
        kudo.kudo_receiver_full_name = 'unknown';
        return kudo;
    });
}

补充说明:

  • 通过在Promise.map(...).then(...).then(...)回调中嵌套.spread()newmaxid仍可通过关闭获得,从而避免使用丑陋的外部变量。
  • 假设Promise.map()是异步的,假设第二次使用
  • postStatus()。如果情况不是这样,那么代码仍然有效,尽管可能会略有不同。