【问题标题】:Express response, async with recursive function快速响应,与递归函数异步
【发布时间】:2020-12-08 13:33:50
【问题描述】:

我正在尝试从 Cloudinary 获取所有照片,但每次调用有 500 个元素的限制。

调用中有一个名为next_cursor 的属性,您可以将其用作分页。

我以递归方式进行,但是当我尝试在 Express 中发送响应超出范围时,关于如何使用递归函数的结果进行响应的任何想法?

谢谢!

const express = require("express");
const router = express.Router();
const cloudinary = require("cloudinary").v2;

const { cloudinarySettings } = require("../config/cloudinary_config");

router.get("/statistics", async (req, res) => {
    function list_resources(results, next_cursor = null) {
        cloudinary.api.resources(
            {
                resource_type: "image",
                mex_results: 500,
                next_cursor: next_cursor,
            },
            function (err, res) {
                console.log(err);
                res.resources.forEach(function (resource) {
                    results.push(resource);
                });

                if (res.next_cursor) {
                    list_resources(results, res.next_cursor);
                } else {
                    return res.status(200).json(results)
                }
            }
        );
    }

    const results = [];
    list_resources(results);
});

【问题讨论】:

    标签: javascript express recursion callback cloudinary


    【解决方案1】:

    您需要修改您的list_resources 函数以返回如下承诺,然后使用async-await 等待结果,然后再提交res

    router.get("/statistics", async (req, res) => {
        async function list_resources(results, next_cursor = null) {
            await new Promise((resolve) => {
                cloudinary.api.resources(
                    {
                        resource_type: "image",
                        mex_results: 500,
                        next_cursor: next_cursor,
                    },
                    function (err, res) {
                        if (err) {
                            console.log(err);
                            resolve();
                            
                        } else {
                            res.resources.forEach(function (resource) {
                                results.push(resource);
                            });
    
                            if (res.next_cursor) {
                                list_resources(results, res.next_cursor).then(() => resolve());
                            } else {
                                resolve();
                            }
                        }
                        
                    }
                );
            });
        }
    
        const results = [];
        await list_resources(results);
        return res.status(200).json(results);
    });
    

    【讨论】:

      【解决方案2】:

      我可能会以不同的方式分解问题,并创建一个更简单的递归函数来返回结果,而不是修改传入的数组。我还将将该函数移出router.get 回调以保持整洁。

      所以它可能看起来像这样:

      const listResources = async ({resource_type, max_results = 500, next_cursor = null} = {}) => {
        return new Promise((resolve, reject) => {
          cloudinary .api .resources (
            {resource_type, max_results, next_cursor}, 
            async (err, {resources, next_cursor}) => {
              try {
                if (err) {reject (err)}
                else resolve ([
                  ...resources, 
                  ...(next_cursor ? await listResources ({resource_type, max_results, next_cursor}) : [])
                ])
              } catch (err) {reject (err)}
            }
          )
        })
      }
      
      router.get('/statistics', async (req, res) => {
        try {
          const results = await listResources ({resource_type: 'image'})
          res .status (200) .json (results)
        } catch (err) {
          console .warn (err)
          res .status (404) // or 500, or whatever, based on error
        }
      })
      

      最有趣的部分是:

          resolve ([
            ...resources, 
            ...(next_cursor ? await listResources ({resource_type, max_results, next_cursor}) : [])
          ])
      

      如果我们返回一个next_cursor 值,我们会在解析它之前将使用它的递归调用的结果附加到我们当前的结果中。如果我们没有得到一个,我们追加一个空数组。

      如果能够编写这个可以说更简单的云回调函数,那就太好了:

          async (err, {resources, next_cursor}) => err 
            ? reject (err)
            : resolve ([
                ...resources, 
                ...(next_cursor ? await listResources ({resource_type, max_results, next_cursor}) : [])
              ])
      

      避免使用 try-catchif-else 块。但我找不到直接的方法来做到这一点并将错误传递回最初调用listResources的函数。

      您可以在浏览器中使用此 sn-p 中的 cloudinaryrouter 的虚拟实现进行测试:

      const listResources = async ({resource_type, max_results, next_cursor = null}) => {
        return new Promise((resolve, reject) => {
          cloudinary.api.resources(
            {resource_type, max_results, next_cursor}, 
            async (err, {resources, next_cursor}) => {
              try {
                if (err) {reject (err)}
                else resolve ([
                  ...resources, 
                  ...(next_cursor ? await listResources ({resource_type, max_results, next_cursor}) : [])
                ])
              } catch (err) {reject (err)}
            }
          )
        })
      }
      
      router.get('/statistics', async (req, res) => {
        try {
          const results = await listResources ({resource_type: 'image', max_results: 3})
          res .status (200) .json (results)
        } catch (err) {
          console .warn (err)
          res .status (500) // or whatever, based on error
        }
      })
      .as-console-wrapper {max-height: 100% !important; top: 0}
      <script>
      // Dummy implementations for testing:
      
      const cloudinary = (() => {
        const vals = [{t: 'image', id: 1,  v: 'a'}, {t: 'image', id: 2,  v: 'b'}, {t: 'image', id: 3,  v: 'c'}, {t: 'pdf',   id: 4,  v: 'd'}, {t: 'image', id: 5,  v: 'e'}, {t: 'image', id: 6,  v: 'f'}, {t: 'video', id: 7,  v: 'g'}, {t: 'image', id: 8,  v: 'h'}, {t: 'image', id: 9,  v: 'i'}, {t: 'image', id: 10, v: 'j'}, {t: 'image', id: 11, v: 'k'}, {t: 'image', id: 12, v: 'l'}]; 
        const last = (xs) => xs.length ? xs[xs.length - 1] : undefined; 
        return {api: {
          resources: ({resource_type, max_results, next_cursor = 0} , fn) => {
            const res = vals .filter (({t, id}) => t == resource_type && id > next_cursor).slice(0, max_results)
      //      fn (next_cursor == 3 ? 'houston, we have a problem' : null, {  // *** use to test an error condition ***
            fn (null, {
              resources: res, 
              ...(res.length > 0 && last(res).id < last(vals).id ? {next_cursor: last(res).id} : {})
            })
          }
        }}
      })()
      
      const router = {
        get: (path, fn) =>
          fn (
            {},
            {status: (n) => {
              console.log(`returning ${n} for request to ${path}`)
              return {
                json: val => console.log(`Body: \n${JSON.stringify(val, null, 4)}`)
              }
            }}
          )
      }
      </script>

      如果您取消注释虚拟 cloudinary 代码中的替代行并注释前一行,您可以看到cloudinary .api .resources 引发错误时的行为。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-17
        • 1970-01-01
        • 2016-09-21
        • 2018-11-18
        • 2018-02-24
        • 2023-03-23
        相关资源
        最近更新 更多