【问题标题】:JS Promise API: Create a loop of promises and merge result into another objectJS Promise API:创建一个 Promise 循环并将结果合并到另一个对象中
【发布时间】:2017-11-18 01:20:01
【问题描述】:

我正在尝试了解 Promise API。

测试用例: 我正在使用来自jsonplaceholder 的三个API。

/user/{userId} #Returns a user
/posts?userId=1 #Returs list of posts by user
/comments?postId=1 #Returns list of comments for the post

我需要将所有三个 API 的输出组合成如下结构。

var user = {
    'id' : "1",
    "name" : "Qwerty",
    "posts" : [
        {
            "id" : 1,
            "userid" : 1,
            "message" : "Hello",
            "comments" : [
                {
                    'id' : 1,
                    "postId" :1
                    "userid" : 2,
                    "message" : "Hi!"                   
                },
                {
                    'id' : 2,
                    "postId" :1
                    "userid" : 1,
                    "message" : "Lets meet at 7!"
                }
            ]
        }
    ]
}

我面临的挑战是将 cmets 合并到每个帖子。请帮忙。我的意思是我不确定该怎么做。

当前实施。

var xyz = function (userId) {
    return Promise.all(
        [
            usersApi.getUsersByIdPromise(userId),
            postsApi.getPostsForUserPromise(userId)
        ]
    ).spread((user, posts) => {
        user.posts = posts;

        for (let post of user.posts){
            commentsApi.getCommentsForPostPromise(post.id)
                .then(comments => {                   
                    //Merge comments to post
                    //How do i return a merged user object
                });
        }        
    })
}

【问题讨论】:

  • 您尝试对Promise.all 的结果使用的spread 方法是什么?标准 Promise API 中没有 spread
  • 它的bluebirdjs.com,请在标准promise API中给出解决方案。

标签: javascript promise bluebird es6-promise


【解决方案1】:

你在正确的轨道上,见 cmets:

var xyz = function (userId) {
    // Start parallel requests for user and posts
    return Promise.all(
        [
            usersApi.getUsersByIdPromise(userId),
            postsApi.getPostsForUserPromise(userId)
        ]
    ).then(([user, posts]) => { // Note the destructuring
        // We have both user and posts, let's add the posts to the user
        user.posts = posts;

        // Send parallel queries for all the post comments, by
        // using `map` to get an array of promises for each post's
        // comments
        return Promise.all(user.posts.map(post => 
            commentsApi.getCommentsForPostPromise(post.id)
                .then(comments => {                   
                    // Have the comments for this post, remember them
                    post.comments = comments;
                })
            ))
            // When all of those comments requests are done, set the
            // user as the final resolution value in the chain
            .then(_ => user);
    });
};

例子:

// Mocks
var commentId = 0;
var usersApi = {
    getUsersByIdPromise(userId) {
        return new Promise(resolve => {
            setTimeout(_ => resolve({id: userId, name: "Some User"}), 100);
        });
    }
};
var postsApi = {
    getPostsForUserPromise(userId) {
        return new Promise(resolve => {
            setTimeout(_ => resolve([
                {userId: userId, id: 1, title: "Post 1"},
                {userId: userId, id: 2, title: "Post 2"}
            ]), 100);
        });
    }
};
var commentsApi = {
    getCommentsForPostPromise(postId) {
        return new Promise(resolve => {
            setTimeout(_ => resolve([
                {postId: postId, id: ++commentId, title: "First comment on post id = " + postId},
                {postId: postId, id: ++commentId, title: "Second comment on post id = " + postId},
                {postId: postId, id: ++commentId, title: "Third comment on post id = " + postId}
            ]), 100);
        });
    }
};
// Code
var xyz = function (userId) {
    // Start parallel requests for user and posts
    return Promise.all(
        [
            usersApi.getUsersByIdPromise(userId),
            postsApi.getPostsForUserPromise(userId)
        ]
    ).then(([user, posts]) => { // Note the destructuring
        // We have both user and posts, let's add the posts to the user
        user.posts = posts;

        // Send parallel queries for all the post comments, by
        // using `map` to get an array of promises for each post's
        // comments
        return Promise.all(user.posts.map(post => 
            commentsApi.getCommentsForPostPromise(post.id)
                .then(comments => {                   
                    // Have the comments for this post, remember them
                    post.comments = comments;
                })
            ))
            // When all of those comments requests are done, set the
            // user as the final resolution value in the chain
            .then(_ => user);
    });
};
// Using it
xyz().then(user => {
    console.log(JSON.stringify(user, null, 2));
});
.as-console-wrapper {
  max-height: 100% !important;
}

虽然实际上,我们可以在收到帖子后立即开始向 cmets 请求帖子,而无需等到用户之后:

var xyz = function (userId) {
    return Promise.all(
        [
            usersApi.getUsersByIdPromise(userId),
            postsApi.getPostsForUserPromise(userId).then(posts =>
                // We have the posts, start parallel requests for their comments
                Promise.all(posts.map(post => 
                    commentsApi.getCommentsForPostPromise(post.id)
                        .then(comments => {
                            // Have the post's comments, remember them
                            post.comments = comments;
                        })
                ))
                // We have all the comments, resolve with posts
                .then(_ => posts)
            )
        ]
    ).then(([user, posts]) => { // Note the destructuring
        // We have both user and posts (with their filled-in comments)
        // Remember the posts on the user, and return the user as the final
        // resolution value in the chain
        user.posts = posts;
        return user;
    });
};

例子:

// Mocks
var commentId = 0;
var usersApi = {
    getUsersByIdPromise(userId) {
        return new Promise(resolve => {
            setTimeout(_ => resolve({id: userId, name: "Some User"}), 100);
        });
    }
};
var postsApi = {
    getPostsForUserPromise(userId) {
        return new Promise(resolve => {
            setTimeout(_ => resolve([
                {userId: userId, id: 1, title: "Post 1"},
                {userId: userId, id: 2, title: "Post 2"}
            ]), 100);
        });
    }
};
var commentsApi = {
    getCommentsForPostPromise(postId) {
        return new Promise(resolve => {
            setTimeout(_ => resolve([
                {postId: postId, id: ++commentId, title: "First comment on post id = " + postId},
                {postId: postId, id: ++commentId, title: "Second comment on post id = " + postId},
                {postId: postId, id: ++commentId, title: "Third comment on post id = " + postId}
            ]), 100);
        });
    }
};
// Code
var xyz = function (userId) {
    return Promise.all(
        [
            usersApi.getUsersByIdPromise(userId),
            postsApi.getPostsForUserPromise(userId).then(posts =>
                // We have the posts, start parallel requests for their comments
                Promise.all(posts.map(post => 
                    commentsApi.getCommentsForPostPromise(post.id)
                        .then(comments => {
                            // Have the post's comments, remember them
                            post.comments = comments;
                        })
                ))
                // We have all the comments, resolve with posts
                .then(_ => posts)
            )
        ]
    ).then(([user, posts]) => { // Note the destructuring
        // We have both user and posts (with their filled-in comments)
        // Remember the posts on the user, and return the user as the final
        // resolution value in the chain
        user.posts = posts;
        return user;
    });
};
// Using it
xyz().then(user => {
    console.log(JSON.stringify(user, null, 2));
});
.as-console-wrapper {
  max-height: 100% !important;
}

【讨论】:

  • 你应该把post.comments = post改成post.comments = comments
  • @nicowernli:大声笑,是的,是的,我应该,谢谢。 :-) (编辑:固定)
  • @T.J.Crowder 抱歉,两者都不适合我,我作为节点应用程序运行。也没有错误。 .then(_ => { console.log(posts);resolve(posts)}) 这里没有打印任何内容。可能是环境有问题。另外,我必须将[user, posts] 更改为(user, posts)
  • @titogeo:在第二个示例中,我有一个小语法错误(你需要 () 围绕解构参数)和一个小的嵌套错误。这里不应该是特定于环境的任何东西。我已经修复了上面的那些小错误并添加了实时示例。
  • @titogeo:v2 允许更多重叠,因此可以更快地完成:如果对帖子的查询在用户查询之前完成,它不会等到用户查询完成后再请求 cmets . v1 等到 post 和 user 都在,然后才请求 cmets。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-09
  • 2020-12-08
  • 2021-09-07
  • 2019-06-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多