首页 > 解决方案 > NodeJS - 如何使 res.render() 在函数之后运行而不是异步运行

问题描述

我做了一个简单的应用程序,它的 UI 显示一个输入文本和两个按钮:获取和显示。

一般的想法是在用户键入一个单词并单击“获取”后,应用程序会从 Datamuse API 请求含义类似的单词,并以 JSON 格式返回单词及其标签(名词、动词、形容词......)。

我希望如果应用程序成功获取这些单词,那么成功文本将位于名为“fetchStatus”的变量中。

之后,我希望“fetchStatus”的值将在 res.render 中传输到具有 UI 代码的 index.ejs,以便用户可以查看从 Datamuse 获取是否成功。

运行 fetch 的函数称为“FetchData(req.body.wordInput)”。

问题是 res.render 函数在 FetchData 函数之前运行,因此 fetchStatus 值是一个空字符串。

在函数 FetchData 我试图返回字符串并将其传递给 res.render 但没有成功。

如果我将“fetchStatus”作为参数或全局变量,也会发生同样的情况。

app.js
============
//jshint esversion:6

var fetchStatus = "";

var vocabularyTags = {
  syn: {name:"synonym", freq:0},
  n: {name:"noun", freq:0},
  v: {name:"verb", freq:0},
  adj:{name:"adjective", freq:0},
  adv:{name:"adverb", freq:0},
  u:{name:"undetermined", freq:0},
  prop: {name:"propery", freq:0}
};



const express = require("express");
const bodyParser = require("body-parser");
const request = require("request");

var app = express();

// Setting ejs
app.set('view engine', 'ejs');

// Setting body-parser
app.use(bodyParser.urlencoded({ extended: true }));



app.get("/",function(req,res){
  // res.sendFile(__dirname+"/index.html");
  res.render("index", {fetchStatus:"", vocabularyTags:{}});


});

app.post("/",function(req,res){
  // var fetchStatus = "";
  var buttonPressed = req.body.submit;

  if (buttonPressed === "fetch") {
    FetchData(req.body.wordInput);
    console.log("Fetch Status: "+fetchStatus);
    res.render("index",{ fetchStatus:fetchStatus ,vocabularyTags:{} });
  }
  else if (buttonPressed === "show") {
    //ShowData();
    var vocabularyTagsResults = vocabularyTags;
    res.render("index",{fetchStatus:"" ,vocabularyTags:vocabularyTagsResults});

    // Clear Vocabulary Tags Frequencies
    for (var key in vocabularyTags) {
      vocabularyTags[key].freq = 0;
    }



  }

});



app.listen(3000,function(){
  console.log("Server is running on port 3000");
});


function FetchData(wordInput) {
    // var fetchStatus = "";
    var options = {
      url:"https://api.datamuse.com/words",
      method:"GET",
      qs: {
        ml: wordInput
      }
    };

    request(options,function(error,response,body){

      if (error) {
        fetchStatus = error;
      }
      else {
        var dataMuseML = JSON.parse(body);
        console.log(dataMuseML);

        // Counting the related tags
        dataMuseML.forEach(function(item){
          var tags = item.tags;
          tags.forEach(function(tag){
            vocabularyTags[tag].freq++;
          });
        });
        fetchStatus = "Fetch from Datamuse API has succeeded!";
      }
    });

    // return fetchStatus;
}


views/index.ejs
================
<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8">
    <title>Datamuse API</title>
  </head>

  <body>
    <h1>Datamuse API</h1>

    <form action="/" method="post">
      <input type="text" name="wordInput" placeholder="Enter a word...">
      <button type="submit" name="submit" value="fetch">Fetch</button>
      <button type="submit" name="submit" value="show">Show</button>
    </form>

    <h3> <%= fetchStatus %> </h3>


    <% for (var key in vocabularyTags) { %>
      <p> <span><%= vocabularyTags[key].name %>:</span> <span> <%= vocabularyTags[key].freq %></span> </p>
    <% } %>


  </body>


</html>

预期的结果是“fetchStatus”将有一个成功或错误文本,所以我可以将它传递给 res.render,它将把它放在 index.ejs 中。

标签: javascriptnode.js

解决方案


你应该使用Promise

function FetchData(wordInput) {
    // var fetchStatus = "";
    var options = {
      url:"https://api.datamuse.com/words",
      method:"GET",
      qs: {
        ml: wordInput
      }
    };

    return new Promise((resolve, reject) => {
        request(options,function(error,response,body){

          if (error) {
            reject(error);
          }
          else {
            var dataMuseML = JSON.parse(body);
            console.log(dataMuseML);

            // Counting the related tags
            dataMuseML.forEach(function(item){
              var tags = item.tags;
              tags.forEach(function(tag){
                vocabularyTags[tag].freq++;
              });
            });
            resolve("Fetch from Datamuse API has succeeded!");
          }
        });
      });
    // return fetchStatus;
}

然后你可以这样称呼它:

app.post("/",function(req,res){
  // var fetchStatus = "";
  var buttonPressed = req.body.submit;

  if (buttonPressed === "fetch") {
    FetchData(req.body.wordInput).then(res => {
      console.log("Fetch Status: "+res);
      res.render("index",{ fetchStatus:res ,vocabularyTags:{} });
    }).catch(err => {
      console.log("Error: "+err);
      res.render("index",{ fetchStatus:err ,vocabularyTags:{} });
    })
  }
  // ...
}

如果您想将结果保存在全局中,您可以设置您的 fetchStatus,resolve然后then像这样调用:

// ...
return new Promise((resolve, reject) => {
      if (error) {
        fetchStatus = error;
      }
      else {
        // ...
        fetchStatus = "Fetch from Datamuse API has succeeded!";
      }
    });
  });
  resolve(fetchStatus);
}

app.post("/",function(req,res){
  if (buttonPressed === "fetch") {
    FetchData(req.body.wordInput).then(res => {
      // res or fetchStatus should be available here but it's better to use res
      console.log("Fetch Status: "+res);
      res.render("index",{ fetchStatus:res ,vocabularyTags:{} });
    })
  }
}

推荐阅读