【问题标题】:how to synchronize functions with promises in node.js如何在 node.js 中将函数与 Promise 同步
【发布时间】:2018-01-19 16:08:11
【问题描述】:

嗨,我正在尝试将我的函数与将 转换为 同步。
我想通过 forEach 循环添加到所有帖子、post.authorName 字段并查询到 user collection
首先我尝试使用回调,但这是,我需要 工具。
所以我使用,但我的结果仍然像回调。
这是我的代码:

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

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

【问题讨论】:

  • 能否将问题简化为具体问题并提供minimal reproducible example
  • 你只能回答一个问题:promise能保证同步编程吗?
  • 不,当然不是。 Promise 是处理异步的一种更好的方法。您不应该尝试使异步任务同步。
  • 避免使用 Promise 构造函数反模式 - getPostAuthorName 返回一个 Promise,无需将调用包装在一个 Promise 中……反过来,findOnemongo.connect 也返回 Promise,所以,也不需要将它们包装在 Promise 构造函数中
  • does promise guarantee sync programing - 恰恰相反,承诺保证异步编程

标签: callback promise async sync promise javascript node.js asynchronous promise synchronization


【解决方案1】:

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

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 关键字同步 Promise(不要忘记将您的函数 async)。示例:

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

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

【讨论】:

    【解决方案2】:

    您的问题在于这个(严重缩进)代码

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

    正确缩进看起来像

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

    所以很明显,resolve 相对于 getPostAuthorName 被“同步”调用 - 但在 getPostAuthorName.then 之前(异步调用)可能被调用 - 因此为什么你的 promises 数组都解决得太早了

    所以,如果你移动它

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

    现在,您的代码应该按预期运行

    解决代码中的“promise 构造函数反模式” - 以上是一个示例

    由于getPostAuthorName返回一个Promise,所以不需要这样做

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

    这相当于

    const promise = getPostAuthorName(post.authorID).then(function(postAuthor){
        post.authorName = postAuthor;
        return; // returns "undefined", just like your resolve() results in
    });
    

    所以,删除所有这些反模式,并使用

    Promise.all(posts.map(
    

    而不是用 push 构建数组

    会产生类似的代码

    const mongo = require('mongodb').MongoClient();
    const url = "mongodb://localhost:27017/blog";
    const ObjectId = require('mongodb').ObjectID;
    
    const listPosts = function(req, res) {
        find('post', {}, 10, {author: 1})
        .then(posts => 
            Promise.all(posts.map(post => 
                getPostAuthorName(post.authorID)
                .then(postAuthor => post.authorName = postAuthor)
            ))
        )
        .then(() => console.log('i should print at end' + '\n'));
    }
    
    const getPostAuthorName = authorID => 
        findOne('user', {_id: new ObjectId(authorID)})
        .then(result => result.name);
    
    
    const find = (collection, cond = {}, limit = 0, sort = {}) => 
        mongo.connect(url) 
        .then(db => 
            db.collection(collection)
            .find(cond)
            .limit(limit)
            .sort(sort)
            .toArray()
        );
    
    const findOne = (collection, cond = {}) => 
        mongo.connect(url)
        .then(db => 
            db.collection(collection)
            .findOne(cond)
        );
    

    我想我又掉进了陷阱.. 我敢打赌 posts 不是 javacript 数组 - 在那种情况下我会做一个类似的函数

    const makeArray = collection => {
        const ret = [];
        collection.forEach(item => ret.push(item));
        return ret;
    };
    

    改变

            Promise.all(posts.map(post => 
    

            Promise.all(makeArray(posts).map(post => 
    

    【讨论】:

      【解决方案3】:

      如果您不需要,请不要创建承诺!相反,利用链式承诺的能力:

      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 构造函数创建不必要的承诺称为explicit-construction anti-pattern

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

      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;  
        });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-01-15
        • 2016-11-21
        • 2016-07-02
        • 2019-10-11
        • 1970-01-01
        • 2015-10-15
        • 1970-01-01
        • 2017-05-18
        相关资源
        最近更新 更多