你如何获得javascript承诺与node + express一起使用并显示在EJS文件上?

时间:2018-02-07 22:05:51

标签: javascript html node.js asynchronous ejs

在谈到文档之前,我已经和他们一点也没有帮助。

我有一个以节点为骨干的网页。在一个页面上,我需要从NASA的当日天文图片(APOD)API请求过去的10张图像,然后我需要从Launch Library API请求接下来的5次即将发布({{3} })。我的问题是并不是所有的APOD都会加载(我理解这是因为异步请求的性质)。

这是我对Node后端的简洁app.js文件:

app.get("/index", function(req, res) {
    /**Requesting NASA's Astronomy Picture of the Day**/
    var apod_url = "https://api.nasa.gov/planetary/apod?api_key=[My Key]"
    var apod_img_urls = [];
    var curr_moment = moment();
    for(var i = 0; i < 10; i++) {
        var appended_url = apod_url + "&date=" + curr_moment.subtract(i, "days").format("YYYY-MM-DD");
        request(appended_url, function(error, reponse, body) {
            if(!error && reponse.statusCode == 200) {
                var img_json = JSON.parse(body);
                if(img_json.media_type == "image") {
                    var apod_promise = new Promise(function(resolve, reject){
                        resolve(img_json.hdurl);
                    });
                    apod_img_urls.push(apod_promise);
                }
            } else {
                console.log(error);
            }
        });
    }
    /**************************************************/

    var url = "https://launchlibrary.net/1.3/launch?next=20&mode=verbose";
    request(url, function(error, response, body) {
       if(!error && response.statusCode == 200) {
           var data = JSON.parse(body);
           res.render("index", {data: data, apod_img_urls: apod_img_urls});
       } else {
           console.log(error);
       }
    });
});

这是一个EJS代码段

<% apod_img_urls.forEach(function(promise, index) { %>
    <div class="carousel-item <%= (index == 0 ? 'active' : '') %>">
        <div class="w-100 home-image" style="background-image: url('<%= promise.then(function(url) {return url}); %>')"></div>
    </div>
<% }); %>

当我检查来源时,它显示div的背景图片网址是[object Promise]。我拥有它的方式,没有图像出现。显示的div的数量(即我应该拥有的图像的数量)也是可变的;有时它是5,有时它是3,有时则是3。我的问题可能是我在另一个请求中呈现页面吗?另外,如何才能将实际图像URL显示在EJS文件中?

2 个答案:

答案 0 :(得分:2)

你正在创建承诺太迟了,在reequest的异步回调中 - 需要相当简单的代码重组

一旦所有承诺都在一个数组中,你需要等待它们完成,使用Promise.all

app.get("/index", function(req, res) {
    /**Requesting NASA's Astronomy Picture of the Day**/
    var apod_url = "https://api.nasa.gov/planetary/apod?api_key=[My Key]"
    var promises = [];
    var curr_moment = moment();
    for(var i = 0; i < 10; i++) {
        var appended_url = apod_url + "&date=" + curr_moment.subtract(i, "days").format("YYYY-MM-DD");
        promises.push(new Promise((resolve, reject) => {
            request(appended_url, function(error, reponse, body) {
                if(!error && reponse.statusCode == 200) {
                    var img_json = JSON.parse(body);
                    resolve(img_json.hdurl);
                } else {
                    reject(error);
                    console.log(error);
                }
            });
        }));
    }
    Promise.all(promises).then(apod_img_urls => {
        var url = "https://launchlibrary.net/1.3/launch?next=20&mode=verbose";
        request(url, function(error, response, body) {
           if(!error && response.statusCode == 200) {
               var data = JSON.parse(body);
               res.render("index", {data, apod_img_urls});
           } else {
               console.log(error);
           }
        });
    });
});

除了使用请求承诺和它所需要的所有内容之外,您始终可以创建自己的有效request函数,在这种情况下,如果状态不是200

const requestP = url => new Promise((resolve, reject) => request(url, (error, response, body) => {
    if (error) {
        reject({error, response, body});
    } else if (resonse.statusCode != 200) {
        reject(resonse.statusCode);
    } else {
        resolve({response, body});
    }
}));

现在,您的代码可以写成:

app.get("/index", function(req, res) {
    /**Requesting NASA's Astronomy Picture of the Day**/
    const apod_url = "https://api.nasa.gov/planetary/apod?api_key=[My Key]"
    const curr_moment = moment();
    Promise.all(Array.from({length:10}, (_, i) => {
        const appended_url = apod_url + "&date=" + curr_moment.subtract(i, "days").format("YYYY-MM-DD");
        return requestP(appended_url).then(({response, body}) => JSON.parse(body).hdurl);
    })).then(apod_img_urls => {
        const url = "https://launchlibrary.net/1.3/launch?next=20&mode=verbose";
        return requestP(url).then(({response, body}) => {
            const data = JSON.parse(body);
            return res.render("index", {data, apod_img_urls});
        });
    });
});   

注意:那里有很多ES2015 +正在进行中

答案 1 :(得分:1)

如果您使用承诺返回请求库,如request-promise,那么您可以执行以下操作:

app.get("/index", function(req, res) {
    var apod_url = "https://api.nasa.gov/planetary/apod?api_key=[My Key]"
    var curr_moment = moment();
    var urls = [];
    for(var i = 0; i < 10; i++) {
        urls[i] = apod_url + "&date=" + curr_moment.subtract(i, "days").format("YYYY-MM-DD");
    }
    Promise.all(urls.map(url => rp({ url, json: true})).then((results) => {
      // here you have all results with JSON already parsed for you
      // ...
    }).catch((err) => {
      // handle error
      // make sure to return response to client
      // ...
    });
    // ...
});

如果您想使用promises,请使用承诺返回模块,例如request-promiseaxios,而不是标准request,请参阅:

或者,使用bluebird.promisify或内置util.primisify(自8.0开始)宣传request模块,请参阅:

有关宣传request的更多选项,请参阅:

然后,当你有一个promise-returns请求库时,根据需要创建一个URL数组,然后使用请求函数映射该数组,如下所示:

let rp = require('request-promise');
let urls = ['http://...', 'http://...'];
let promises = urls.map(rp);

然后使用Promise.all等待所有这些内容完成,同时完成:

Promise.all(promises)
  .then(...)
  .catch(...);

或者,如果您正在使用async / await,那么:

try {
  let x = await Promise.all(promises);
  ...
} catch (err) {
  ...
}

如果您正确运行,axiosrequest-promise-jsonrequest-promise等许多模块都会为您解析JSON,请参阅:

避免自己解析JSON,但如果你这样做,总是JSON.parse()放在try/catch内(或使用我的小tryjson模块) - 请参阅知道原因: