如何在 node.js 中同步函数和 promises

新手上路,请多包涵

您好,我正在尝试将我的函数与 convert callback to promise 同步。

我想通过 forEach 循环添加到所有帖子, post.authorName 字段并查询 用户集合

首先我尝试使用回调,但这是 异步 的,我需要一个 同步 工具。

所以我使用 promise 但我的结果仍然像回调。

这是我的代码:

 var mongo = require('mongodb').MongoClient();
var url = "mongodb://localhost:27017/blog";
var ObjectId = require('mongodb').ObjectID;

var listPosts = function(req, res) {
    find('post', {}, 10, {author: 1})
        .then(function(posts) {

            var myPosts = posts;

            const promises = [];

            myPosts.forEach(function(post) {

                console.log("hi i'm forEach" + '\n');
                console.log(post);
                console.log('\n');

                const promise = new Promise(function(resolve, reject){
                    getPostAuthorName(post.authorID)
                        .then(function(postAuthor){
                            post.authorName = postAuthor;
                        })
                        resolve();
                });

                console.log("i'm end of forEach and this is result:");
                console.log(post);
                console.log('\n');

                promises.push(promise);
            });

            Promise.all(promises).then(() => {

                console.log('i should print at end' + '\n');

            });
        });
}

var getPostAuthorName = function(authorID) {
    return new Promise(function(resolve, reject){
        findOne('user', {_id: new ObjectId(authorID)})
            .then(function(result){

                console.log("i'm getPostAuthorName" + '\n');

                resolve(result.name);
            })
    })
}

var find = function(collection, cond = {}, limit = 0, sort = {}) {
    return new Promise(function(resolve, reject){
        mongo.connect(url)
            .then(function(db){
                db.collection(collection)
                    .find(cond).limit(limit).sort(sort).toArray()
                        .then(function(result){
                            resolve(result);
                        })
            })
    });
}

var findOne = function(collection, cond = {}){
    return new Promise(function(resolve, reject){
        mongo.connect(url)
            .then(function(db){
                db.collection(collection).findOne(cond)
                    .then(function(result){

                        console.log("i'm findOne" + '\n');

                        resolve(result);
                    })
            })
    })
}

listPosts();

最后我收到了这个结果:

 hi i'm forEach

{ _id: 59888f418c107711043dfcd6,
  title: 'FIRST',
  content: 'this is my FIRST post',
  timeCreated: 2017-08-07T16:03:13.552Z,
  authorID: '5987365e6d1ecc1cd8744ad4' }

i'm end of forEach and this is result:
{ _id: 59888f418c107711043dfcd6,
  title: 'FIRST',
  content: 'this is my FIRST post',
  timeCreated: 2017-08-07T16:03:13.552Z,
  authorID: '5987365e6d1ecc1cd8744ad4' }

hi i'm forEach

{ _id: 598d60d7e2014a5c9830e353,
  title: 'SECOND',
  content: 'this is my SECOND post',
  timeCreated: 2017-08-07T16:03:13.552Z,
  authorID: '5987365e6d1ecc1cd8744ad4' }

i'm end of forEach and this is result:
{ _id: 598d60d7e2014a5c9830e353,
  title: 'SECOND',
  content: 'this is my SECOND post',
  timeCreated: 2017-08-07T16:03:13.552Z,
  authorID: '5987365e6d1ecc1cd8744ad4' }

i should print at end

i'm findOne

i'm getPostAuthorName

i'm findOne

i'm getPostAuthorName

为什么函数不同步运行。有什么解决办法?

原文由 newcomer student 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 707
2 个回答

如果你不需要它们,就不要创建承诺!相反,利用链接承诺的能力:

 var mongo = require('mongodb').MongoClient();
var url = "mongodb://localhost:27017/blog";
var ObjectId = require('mongodb').ObjectID;

var listPosts = function () {
  return find('post', {}, 10, {author: 1})
    .then(function (posts) {
      var promises = posts.map(post => getPostAuthorName(post.authorID));
      return Promise.all(promises).then(names => names.map((name, index) => {
        var post = posts[index];
        post.authorName = name;
        return post;
      });
  });
};

var getPostAuthorName = function(authorID) {
  return findOne('user', {_id: new ObjectId(authorID)}).then(author => author.name);
}

var find = function(collection, cond = {}, limit = 0, sort = {}) {
  return mongo.connect(url)
    .then(db => db.collection(db)
      .find(cond)
      .limit(limit)
      .sort(sort)
      .toArray()
    );
};

var findOne = function(collection, cond = {}) {
  return mongo.connect(url).then(db => db.collection(db).findOne(cond));
};

listPosts().then(posts => console.log('Post:', post, ', author: ', post.authorName));

使用 new Promise 构造函数创建不必要的承诺称为 显式构造反模式

但这不是您的代码中的唯一问题:以下代码段中不必要的承诺使代码变得如此复杂,以至于您没有意识到在找到作者姓名之前您已经解决了承诺:

 const promise = new Promise(function(resolve, reject){
  getPostAuthorName(post.authorID)
    .then(function(postAuthor){
      post.authorName = postAuthor;
    })
  resolve(); // why resolve immediately?
});

相反,它应该是这样的:

 const promise = getPostAuthorName(post.authorID)
  .then(function(postAuthor){
    post.authorName = postAuthor;
  });

原文由 PeterMader 发布,翻译遵循 CC BY-SA 3.0 许可协议

如果你想将回调转换为承诺,你可以简单地做这样的事情:

 function functionWithCallback(params, callback)
{
    [...]
    callback(true);
}

function functionWithPromise(params)
{
    return new Promise((resolve, reject) => {
        functionWithCallback(params, (done) => {
            if (done)
                return resolve(true);
            reject(false);
        });
    });
}

现在,您可以使用 await 关键字同步承诺(不要忘记将您的函数放在 async 中)。例子 :

 async function main()
{
    const p1 = functionWithPromise('1');
    const p2 = functionWithPromise('2');

    await p1;
    await p2;
    console.log('End');
}

原文由 DarkyZ 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题