【问题标题】:How do I make sure a function is completely executed before its value is passed to res.render?如何确保函数在其值传递给 res.render 之前完全执行?
【发布时间】:2019-06-23 02:28:14
【问题描述】:

我想解析地址查询并返回地址及其标题,这样 http://localhost:3000/I/want/title/?address=google.com&address=youtube.com 应返回: google.com - 'google',youtube.com - 'youtube'

我正在使用 Cheerio.js 从 URL 中提取标题,但这需要时间,并且 res.render 行在变量标题填充 URL 标题之前执行。如何确保检索标题的代码在 res.render 之前执行?

到目前为止,我没有收到任何错误,但标题[] 数组在没有数据的情况下发送到我的 .ejs 文件。我尝试通过回调、step.js、async.js 解决这个问题,但似乎没有任何效果。我尝试使用 rsvp.js(promise)解决它,如下所示(来自 app.js),但它也不起作用,titles[] 仍然为空:

app.get("/I/want/title/", function(req,res){


  if (typeof req.query.address === "string"){
  query = [req.query.address];
}
  else {
  query = req.query.address;
}

  var titles=[];

  var promise = new RSVP.Promise(function(resolve, reject) {
      for (i=0;i<(query.length);i++){
        if (!((query[i]).startsWith("https://www."))){
          var url = "https://www." + query[i];
        }else{
          url=query[i];
        }

        request(url, function (err, resp, body) {
          if (err) {
            var title = "NO RESPONSE"
          } else {
          var $ = cheerio.load(body);
          var title = $("title").text();
        }
          titles.push(title);
        });

      }
      resolve(titles);
      reject();
});

promise.then(function(titles) {
  res.render("title", {url: query, siteName: titles});
}).catch(function() {
  console.log("oh no");
});
});

我的语法或逻辑有问题吗?我应该如何使用回调或承诺执行此操作?

【问题讨论】:

  • 你让自己的生活变得艰难,试图用一个new Promise()来做到这一点。即使在回调中使用resolve(totles),第一个响应的request() 也会解析promise,而其他所有的都会被忽略。

标签: javascript node.js promise callback


【解决方案1】:

您必须将 promise 的拒绝和解决放入请求回调中,然后您的代码应该可以正常工作(如下所示)。

app.get('/I/want/title/', function(req, res) {
    if (typeof req.query.address === 'string') {
        query = [req.query.address]
    } else {
        query = req.query.address
    }

    var titles = []

    var promise = new RSVP.Promise(function(resolve, reject) {
        for (i = 0; i < query.length; i++) {
            if (!query[i].startsWith('https://www.')) {
                var url = 'https://www.' + query[i]
            } else {
                url = query[i]
            }

            request(url, function(err, resp, body) {
                if (err) {
                    var title = 'NO RESPONSE'
                    reject()
                } else {
                    var $ = cheerio.load(body)
                    var title = $('title').text()
                }
                titles.push(title)
                resolve(titles)
            })
        }
    })

    promise
        .then(function(titles) {
            res.render('title', {url: query, siteName: titles})
        })
        .catch(function() {
            console.log('oh no')
        })
})

这能回答你的问题吗?

【讨论】:

  • 对不起,我刚刚又测试了一遍,它只在查询中有一个地址时有效。对于两个地址,它要么只返回第一个的标题,要么返回前两个地址查询的第二个标题
  • 要实现这一点,您必须维护一系列承诺。 Fox ex var promises = [new RSVP.Promise1, new RSVP.Promise2,,,] 执行它们然后使用Promise.all(promises).then(...) 参考如何使用 Promise.all() developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • 给定的承诺声明似乎不起作用。你碰巧知道如何使用 RSVP 初始化一个 promise 数组吗?
【解决方案2】:

您的解析应该在请求回调的 else 部分中,而拒绝应该在 if (err) 中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-21
    • 2019-06-30
    • 2012-08-04
    • 2021-01-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多