FCC API 项目。我不明白带有承诺的代码流程

Posted

技术标签:

【中文标题】FCC API 项目。我不明白带有承诺的代码流程【英文标题】:FCC api project. I do not understand the flow of the code with promises 【发布时间】:2021-11-21 07:49:48 【问题描述】:

我被一些代码卡住了,我不明白为什么。我决心不复制和粘贴,而是要了解我在做什么(以及做错了什么)。 问题是我想在 post 路由中创建新的 Url 对象。它生成对象,但不等待函数 getHighestShort 解决其承诺,并且始终将 shortMax 返回为 1。 如果我控制台登录一些代码行,我可以看到 getHighestShort 函数中 if 语句中的代码在创建和保存 Url 对象后被解析,因此 Url 对象的 short_url 始终为 1。 我真的很感激一些正确方向的提示,因为我认为在新的 Url 之前的 await 会使那行代码阻碍其余的代码。 -42 岁的自学者——所以这篇文章听起来可能有点过分了:)

require('dotenv').config();
const express = require('express');
const cors = require('cors');

const bodyParser = require('body-parser')
const mongoose = require('mongoose')
const Url = require('./schemas/urlModel');


mongoose.connect('mongodb://localhost:27017/urlshortener',  useNewUrlParser: true, useUnifiedTopology: true )
  .then(() => console.log('connection open'))
  .catch((err) => console.log(err))

const app = express();

// Basic Configuration
const port = process.env.PORT || 3000;


app.use(cors());

app.use('/public', express.static(`$process.cwd()/public`));

app.use(bodyParser.urlencoded( extended: false ))


app.get('/', function(req, res) 
  res.sendFile(process.cwd() + '/views/index.html');
);

// Your first API endpoint
//find input url in db and return the long version
app.get('/api/shorturl/:input', async (req, res) => 
  try 
    const input = req.params
    // const shortUrlNr = parseInt(input)
    console.log(input)
    console.log(Url.findOne( short_url: `$input` ))
    const found = await Url.findOne( short_url: `$input` )
    res.redirect(`https://$found.full_url`)
   catch (err) 
      console.log(err)
      console.log(res.status)
  
)

app.post('/api/shorturl/new', async (req, res, next) => 
  try 
    const  url  = await req.body
    //check for valid url, if not respond with json object as asked in tests.
    if (!validURL(url)) 
      return res.json(error: 'invalid url')
     else 
      //find url in db and get json response with the corresponding object. 
      //If it is already in the db, than redirect to this website as asked in tests. 
        const foundUrl = await Url.findOne( full_url: `$url` )
        if (foundUrl !== null) 
          res.redirect(`https://$foundUrl.full_url`)
         else 
        //if the url is not there, we''ll create a new entry with that url.
        const newUrl = await new Url( full_url: `$url`,  short_url: `$getHighestShort()` )
        await newUrl.save()
        res.json(newUrl)
         
    
   catch(err) 
    console.log('catch error post request')
    return next(err)
  
)

app.listen(port, function() 
  console.log(`Listening on port $port`);
);

function validURL(str) 
  var pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol
    '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]2,|'+ // domain name
    '((\\d1,3\\.)3\\d1,3))'+ // OR ip (v4) address
    '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path
    '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string
    '(\\#[-a-z\\d_]*)?$','i'); // fragment locator
  return !!pattern.test(str);


function getHighestShort() 
  let shortMax = 1
  //make an array from the collection with short values sorted descending
  //and than pick the first one which should be the highest value.
  //if the array is empty just return 1 as it is the first one
  Url.find().sort(short_url: 1).exec()
    .then(res => 
      if (res.length !== 0) 
        shortMax = res[0].short_url + 1
       
    )
    .catch(err => 
      console.log('catch err server.js function getHighestShort')
      console.log(err)
    )
    return shortMax

型号

const mongoose = require('mongoose')
const Schema = mongoose.Schema

const urlSchema = new Schema( 
    
        full_url: 
            type: String,
            required: false
        ,
        short_url: Number
    
)

const Url = mongoose.model('Url', urlSchema)

module.exports = Url

【问题讨论】:

【参考方案1】:

await 运算符等待promise,以便它像您期望的那样工作。

例如在这一行:

const  url  = await req.body

await 在这里毫无意义,因为req.body 不是一个承诺,而是一个对象。所以它和做的一样:

const  url  = req.body

现在为什么您的代码不起作用?让我们从了解getHighestShort开始,它会返回一个承诺吗? (剧透警告它没有)。

所以首先我们需要让它返回一个承诺(然后我们将不得不等待该承诺解决)。

第一步,让getHighestShort返回一个promise:

function getHighestShort() 
    let shortMax = 1
    //make an array from the collection with short values sorted descending
    //and than pick the first one which should be the highest value.
    //if the array is empty just return 1 as it is the first one
    return new Promise((resolve, reject) => 
        Url.find().sort(short_url: 1).exec()
            .then(res => 
                if (res.length !== 0) 
                    shortMax = res[0].short_url + 1;
                
                resolve(shortMax)
            )
            .catch(err => 
                console.log('catch err server.js function getHighestShort')
                console.log(err)
                // you decide logic here
                resolve(shortMax)
            )
    )

现在我个人建议你重新编写一下,让它更简洁一些,如下所示:

async function getHighestShort() 
    let shortMax = 1
    try 
        //make an array from the collection with short values sorted descending
        //and than pick the first one which should be the highest value.
        //if the array is empty just return 1 as it is the first one
        const results = await Url.find().sort(short_url: 1).limit(1).exec()
        if (res.length !== 0) 
            shortMax = res[0].short_url + 1
        
        return shortMax
     catch (e) 
        console.log('catch err server.js function getHighestShort')
        console.log(err)
        return shortMax
    

请注意,我添加了limit(1),无需在每次调用时读取整个集合。

我想补充一点,您在这里对操作的原子性有一些错误的假设,如果同时处理 2 个请求会发生什么?您将拥有 2 个具有相同 short_url 的网址。

我会把这个问题留给你自己处理。

第 2 步,现在 getHighestShort 返回了一个承诺。我们需要等待它:

const short_url = await getHighestShort();
const newUrl = await new Url( full_url: `$url`,  short_url: short_url )

【讨论】:

感谢您花时间解释事情!

以上是关于FCC API 项目。我不明白带有承诺的代码流程的主要内容,如果未能解决你的问题,请参考以下文章

获取api,为啥我必须在响应json()上使用then,试图理解承诺[重复]

如何使用 YouTube API V3?

PromiseKit - 带有重新加载第一个承诺的可选承诺

Redis - 在带有承诺的事务中插入数组中的值(蓝鸟)

如何执行带有承诺的函数并通过 API 调用多次检查其值并使用该值执行另一个函数?

我不明白为啥带有'connect()'的组件在反应中是有状态的