Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/42.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 在变量中持有承诺的未来价值_Javascript_Node.js_Mongoose_Promise - Fatal编程技术网

Javascript 在变量中持有承诺的未来价值

Javascript 在变量中持有承诺的未来价值,javascript,node.js,mongoose,promise,Javascript,Node.js,Mongoose,Promise,我有一个数据库,需要最近的消息列表。每条消息都是一个对象,并作为这些消息对象的数组存储在chatListNew中 每个消息对象都有一个属性“from”,它是发布消息的用户的ID。我想做的是循环遍历这个数组,并将“From”用户的实际配置文件信息附加到对象本身中。这样,当前端接收到信息时,它就可以访问相应邮件的fromProfile属性中某个特定邮件的发件人配置文件 我想循环浏览每一条并做出承诺。然而,如果只有少数超过数百条的用户发布了数百条消息,那么对每一条来说,这都是非常昂贵的。对每个用户只运

我有一个数据库,需要最近的消息列表。每条消息都是一个对象,并作为这些消息对象的数组存储在chatListNew中

每个消息对象都有一个属性“from”,它是发布消息的用户的ID。我想做的是循环遍历这个数组,并将“From”用户的实际配置文件信息附加到对象本身中。这样,当前端接收到信息时,它就可以访问相应邮件的fromProfile属性中某个特定邮件的发件人配置文件

我想循环浏览每一条并做出承诺。然而,如果只有少数超过数百条的用户发布了数百条消息,那么对每一条来说,这都是非常昂贵的。对每个用户只运行一次mongoose查询将更有意义。所以我发明了一个缓存系统

但是,我对如何在数组元素中存储未来值的承诺感到困惑。我认为将“fromProfile”设置为之前所称的promise会神奇地保持这个承诺,直到值被解析。因此,我使用Promise.all来确保所有承诺都已完成,然后由结果返回,但我存储在数组中的承诺并不是我所希望的值

这是我的密码:

//chatListNew = an array of objects, each object is a message that has a "from" property indicating the person-who-sent-the-message's user ID

let cacheProfilesPromises = []; // this will my basic array of the promises called in the upcoming foreach loop, made for Promise.all
let cacheProfilesKey = {}; // this will be a Key => Value pair, where the key is the message's "From" Id, and the value is the promise retrieving that profile
let cacheProfileIDs = []; // this another Key => Value pair, which basically stores to see if a certain "From" Id has already been called, so that we can not call another expensive mongoose query


chatListNew.forEach((message, index) => {
    if(!cacheProfileIDs[message.from]) { // test to see if this user has already been iterated, if not
        let thisSearch = User.findOne({_id : message.from}).select('name nickname phone avatar').exec().then(results => {return results}).catch(err => { console.log(err); return '???' ; }); // Profile retrieving promise
        cacheProfilesKey[message.from] = thisSearch;
        cacheProfilesPromises.push(thisSearch); // creating the Array of promises
        cacheProfileIDs[message.from] = true;
    }

    chatListNew[index]["fromProfile"] = cacheProfilesKey[message.from]; // Attaching this promise (hoping it will become a value once promise is resolved) to the new property "fromProfile"
});

Promise.all(cacheProfilesPromises).then(_=>{ // Are all promises done?
    console.log('Chat List New: ', chatListNew);
    res.send(chatListNew);
});
这是我的控制台输出:

Chat List New:  [ { _id: '5b76337ceccfa2bdb7ff35b5',
updatedAt: '2018-08-18T19:50:53.105Z',
createdAt: '2018-08-18T19:50:53.105Z',
from: '5b74c1691d21ce5d9a7ba755',
conversation: '5b761cf1eccfa2bdb7ff2b8a',
type: 'msg',
content: 'Hey everyone!',
fromProfile:
 Promise { emitter: [EventEmitter], emitted: [Object], ended: true } },
{ _id: '5b78712deccfa2bdb7009d1d',
updatedAt: '2018-08-18T19:41:29.763Z',
createdAt: '2018-08-18T19:41:29.763Z',
from: '5b74c1691d21ce5d9a7ba755',
conversation: '5b761cf1eccfa2bdb7ff2b8a',
type: 'msg',
content: 'Yo!',
fromProfile:
 Promise { emitter: [EventEmitter], emitted: [Object], ended: true } } ]
而我希望的是:

Chat List New:  [ { _id: '5b76337ceccfa2bdb7ff35b5',
updatedAt: '2018-08-18T19:50:53.105Z',
createdAt: '2018-08-18T19:50:53.105Z',
from: '5b74c1691d21ce5d9a7ba755',
conversation: '5b761cf1eccfa2bdb7ff2b8a',
type: 'msg',
content: 'Hey everyone!',
fromProfile:
 Promise {name: xxx, nickname: abc... etc} },
{ _id: '5b78712deccfa2bdb7009d1d',
updatedAt: '2018-08-18T19:41:29.763Z',
createdAt: '2018-08-18T19:41:29.763Z',
from: '5b74c1691d21ce5d9a7ba755',
conversation: '5b761cf1eccfa2bdb7ff2b8a',
type: 'msg',
content: 'Yo!',
fromProfile:
 {name: xxx, nickname: abc... etc} } ]
谢谢你们!可以通过其他方式实现这一点:)
Pete

承诺
分配给变量时,该变量将始终是
承诺
,除非重新分配该变量。您需要从您的
承诺中获取
承诺的结果。所有
呼叫

对于一个只返回其参数的
.then
,就像你的
.then(results=>{returnresults})
-你可以完全忽略它,它什么也不做

构造承诺数组,还构造一个
from
属性数组,这样每个承诺的
from
对应于另一个数组中相同索引的项。这样,一旦
Promise.all
完成,您就可以将解析值数组转换为由
from
索引的对象,然后您可以迭代
聊天列表new
并将解析值分配给每条消息的
fromProfile
属性:

const cacheProfilesPromises = [];
const messagesFrom = [];

chatListNew.forEach((message, index) => {
  const { from } = message;
  if(messagesFrom.includes(from)) return;
  messagesFrom.push(from);
  const thisSearch = User.findOne({_id : from})
    .select('name nickname phone avatar')
    .exec()
    .catch(err => { console.log(err); return '???' ; });
  cacheProfilesPromises.push(thisSearch);
});

Promise.all(cacheProfilesPromises)
  .then((newInfoArr) => {
    // Transform the array of Promises into an object indexed by `from`:
    const newInfoByFrom = newInfoArr.reduce((a, newInfo, i) => {
      a[messagesFrom[i]] = newInfo;
      return a;
    }, {});

    // Iterate over `chatListNew` and assign the *resolved* values:
    chatListNew.forEach((message) => {
      message.fromProfile = newInfoByFrom[message.from];
    });
  });

承诺是一个对象容器,类似于数组。不同之处在于,承诺所包含的价值有时会被忽略

因此,由于您不知道在Promise行话中何时解析值,通常您会告诉Promise在解析值时如何处理该值

那么比如说,

function (id) {
   const cache = {}
   const promise = expensiveQuery(id)
   // promise will always be a promise no matter what
   promise.then(value => cache[id] = value)
   // After the callback inside then is executed,
   // cache has the value you are looking for,
   // But the following line will not give you the value
  return cache[params.id]
}
现在,要修复该代码,您可以在第一次运行查询时返回承诺,或者返回缓存值

// I moved this out of the function scope to make it a closure
// so the cache is the same across function calls
const cache = {}
function (id) {
   if(cache[id]) return cache[id]
   const promise = expensiveQuery(id)
   // promise will always be a promise no matter what
   promise.then(value => cache[id] = value)
   // now we just return the promise, because the query
   // has already run
  return promise
}
现在,您将获得一个值或承诺,这取决于之前是否为该id调用过一次函数,以及之前的调用是否已解决

但这是一个问题,因为您希望有一个一致的API,所以让我们稍微调整一下

// I moved this out of the function scope to make it a closure
// so the cache is the same across function calls
const cache = {}
function cachingQuery (id) {
   if(cache[id]) return cache[id]
   const promise = expensiveQuery(id)
   // Now cache will hold promises and guarantees that
   // the expensive query is called once per id
   cache[id] = promise
   return promise
}
好的,现在您总是有一个承诺,并且只调用查询一次。记住执行
承诺。然后
不执行另一个查询,它只使用最后一个结果

现在我们有了缓存查询功能,我们可以解决另一个问题了。这就是将结果添加到消息列表中

而且,我们不希望缓存存在的时间太长,因此缓存不能位于最上面的作用域。让我们将所有这些都封装在一个cacheMaker函数中,它将需要一个昂贵的操作来运行,并且它将返回一个函数,该函数将根据其唯一的参数缓存该函数的结果

function makeCacher(query) {
  const cache = {}
  return function (id) {
     if(cache[id]) return cache[id]
     const promise = query(id)
     cache[id] = promise
     return promise
  }
}
现在我们可以尝试解决另一个问题,即为每条消息分配用户

const queryUser = makeCacher((id) => User.findOne({_id : id})
    .select('name nickname phone avatar')
    .exec())

const fromUsers = chatListNew.map((message) => queryUser(message.from))

Promise.all(fromUsers)
  .then(users =>
    chatListNew.map(message =>
         Object.assign(
           {},
           message,
           { fromProfile: users.find(x => x._id === message.from)})))
  .then(messagesWitUser => res.json(messagesWitUser) )
  .catch(next) // send to error handler in express

承诺不是这样的,一切都是这样!如果我是你,我不会将from fromProfile合并到列表中的每个项目中。我会把他们分开,把工作交给客户。您的负载将小得多,如果需要,客户机甚至可以缓存fromProfiles。通过实现所谓的“侧加载”,您仍然可以将其作为一个请求。基本上,您的json将有两个根节点,“fromProfiles”是一个数组,“chatItems”是您现在拥有的数组。感谢您花时间帮助我:)我感谢您。谢谢你帮助我!