Promise的结果是一个数组,如何再去调用一个Promise,返回最终的组合的结果?

我在写一个后台,封装了两个方法去查询数据库,它们的返回都是Promise,调用它们都有正确的结果。

function list(req, res, next) {
    let start = req.query.start;
    let count = req.query.count;
    exchangeDb.queryList(start, count).then(results => {
        res.json(results)
    }).catch(err => {
        throw err;
    });
}


function load(req, res, next, name) {
    let limit = req.query.limit;
    exchangeDb.queryVolume(name, limit).then(results => {
        req.results = util.convert(results);
        return next();
    }).catch(err => {
        throw err;
    });
}

第一个返回的是列表,第二个是返回详情。我想把它们合为一个方法,尝试这样做:

function list(req, res, next) {
    let start = req.query.start;
    let count = req.query.count;
    exchangeDb.queryList(start, count).then(results => {
        results.forEach((index,element) => {
            exchangeDb.queryVolume(element.name,50).then(volume => {
                element.volume = volume;
                results[index] = element;
            })
        });
        res.json(results)
    }).catch(err => {
        throw err;
    });
}

显然,它不能工作,我被这个问题深深的困扰了,如果是链式的Promise,then().then()看上去也不能解决这个问题,请大家帮忙看一下,谢谢。

阅读 4.3k
3 个回答

看起来你是想先取集合,再取集合中每个元素的详情。这个时候,由于取详情的操作也是异步操作,所以你不能直接用 forEach,通常来说,你有 Promise.all 和连接成队列两种做法。

Promise.all

exchangeDb.queryList(start, count)
  .then(results => {
    return Promise.all(results.map(element => {
      return exchangeDb.queryVolume(element.name, 50);
    }));
  })
  .then(results => {
    console.log(results); // <- 这里应该是全部详情数据了
  })
  .catch(err => {
    throw err;
  });

如果并发压力大的话,也可以用队列:

exchangeDb.queryList(start, count)
  .then(results => {
    const all = [];
    const p = results.reduce((p, element) => {
      return p.then(() => {
        return exchangeDb.queryVolume(element.name, 50)
          .then(detail => {
            all.push(detail);
          });
      });
    }, Promise.resolve());
    return all;
  })
  .then(results => {
    console.log(results); // <- 这里应该是全部详情数据了
  })
  .catch(err => {
    throw err;
  });

我之前做过一次讲堂:Promise 的 N 种用法,对 Promise 的讲解比较详细,尤其介绍过几种队列的方式,建议你看一下。

如果我没看错的话,你合并之后的函数应该是在res.json(results)这里返回结果的,很显然是不能正确返回的(results很可能是queryList的结果).因为你内部queryVolume是一个耗时的操作,程序执行到res.json(results) 块时,此时results值并没有被改变。

你的目的是在所有queryVolume都返回结果后,再返回json,因此res.json()一定是一个异步行为。

两个方案:
1.在每个then里面判断是否全部请求完毕

        results.forEach((index,element) => {
        
            const NOT_FILL = '__not_fill'
            
            // 生成一个等长数组,打标记
            const jsonResult = new Array(results.length)
            jsonResult.fill(NOT_FILL)
            
            exchangeDb.queryVolume(element.name,50).then(volume => {
                element.volume = volume;
                jsonResult[index] = element;
                
                // 数组全部有值再返回
                if(!jsonResult.some(element => element === NOT_FILL)){
                    res.json(jsonResult)
                }
            })
        });

2.把循环queryVolume的过程封装成一个新的promiseresolve的条件同1res.json,然后在这个promise的then方法中返回json

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