async/await 函数中的 JavaScript Promise 解析最终响应数组

Posted

技术标签:

【中文标题】async/await 函数中的 JavaScript Promise 解析最终响应数组【英文标题】:JavaScript Promise inside async/await function resolve final array of responses 【发布时间】:2019-04-24 19:39:14 【问题描述】:

我是 javascript 和 Promises 的新手。 我正在尝试构建从 API 获取的对象数组。

为此,我在文件MyFile.js 中构建了两个函数。

当 axios 承诺被解决时,第一个返回一个承诺。这是

function get_items (url) 
    return new Promise((resolve, reject) => 
        let options = 
            baseURL: url,
            method: 'get'
        
        axios(options)
            .then(response => 
                resolve(response.data)
            )
            .catch(error => 
                reject(error.stack)
            )
    )

第二个是这样的:

let output = []
let next_url = 'https://some_url.com/api/data'
async function get_data () 
    try 
        let promise = new Promise((resolve, reject) => 
            if (next_url) 
                get_items(next_url)
                    .then(response => 
                        output.push(...response.results)
                        if (response.next) 
                            next_url = response.next
                            console.log('NEXT_URL HERE', next_url)
                            get_data()
                         else 
                            console.log('else')
                            next_url = false
                            get_data()
                        
                    )
                    .catch(error => 
                        reject(error.stack)
                    )
             else 
                console.log('before resolve')
                resolve(output)
            
        )
        return await promise
     catch(e) 
        console.log(e)
    

这是我磨牙的地方。 我认为我对这个功能的理解是:

它返回了一个承诺的价值(这就是我理解 return await promise 正在做的事情) 这是一个递归函数。因此,如果有 next_url,则函数继续执行。但是如果没有,它会被最后一次调用以进入else 部分,在该部分中它解析数组output,其中包含所有承诺的结果(值而不是状态)。至少,当我执行它并使用我写的 console.log 检查我的健全性检查时,它可以工作。

所以,output 充满了数据,这很棒。

但是,当我从另一个文件MyOtherFile.js 调用这个函数时,像这样:

final_output = []
MyFile.get_data()
    .then(result => 
        console.log('getting data')
        final_output.push(...result)
    )

它永远不会进入then 部分。当我 console.log MyFile.get_data() 时,这是一个未决的承诺。

所以,我想做的是能够让get_data() 等待所有的承诺结果(不使用 Promise.all(),进行连续调用,而不是并行调用,这对表演,我猜?)然后能够在从其他任何地方调用此函数时在then 部分中检索该响应。 请记住,总的来说,我是 Promise 和 JavaScript 的新手(我更喜欢 Python)。

如果我的问题不够清楚,请告诉我。 这两天摸不着头脑,感觉自己在绕圈跑。

感谢您成为一个很棒的社区!

【问题讨论】:

避免Promise constructor antipattern! 【参考方案1】:

这有点未经测试

const api_url = 'https://some_url.com/api/data';

get_data(api_url).then((results) => 
  console.log(results);
).catch((error) => 
   // console.error(error);
);

function get_items (url) 
  const options = 
    baseURL: url,
    method: 'get'
  ;

  return axios(options).then((response) => response.data);


async function get_data(next_url) 
  const output = [];

  while (next_url) 
    const  results, next  = await get_items(next_url);
    output.push(...results);
    next_url = next;
  

  return output;

基本上它使事情变得更整洁。我建议查看更多有关 Promises 的示例以及优势以及何时缓解等待/异步。需要记住的一件事是,如果您返回一个 Promise,它将遵循整个 then 链,并且它将始终返回一个值为最后一个 then.. 的 Promise,如果这有意义:)

【讨论】:

【参考方案2】:

有几个问题。一个是你永远不会 resolve 初始 Promise 除非输入 else 块。另一个是您应该每次返回递归get_data 调用,以便它可以与初始Promise 正确链接。您也可以考虑避免explicit promise construction antipattern - get_items 已经返回Promise,因此无需构造另一个(get_items 内部相同,axios 调用也返回Promises)。

您可以考虑一个普通的while 循环,重新分配next_url 字符串,直到它是假的:

function get_items (baseURL) 
  const options = 
    baseURL: url,
    method: 'get'
  
  // return the axios call, handle errors in the consumer instead:
  return axios(options)
    .then(res => res.data)


async function get_data() 
  const output = []
  let next_url = 'https://some_url.com/api/data'
  try 
    while (next_url) 
      const response = await get_items(next_url);
      output.push(...response.results)
      next_url = response.next;
    
   catch (e) 
    // handle errors *here*, perhaps
    console.log(e)
  
  return output;

注意.catch 将导致Promise拒绝 Promise 转换为已解决 - 你不想@987654338 @ 无处不在,因为这会使调用者难以检测到错误。

【讨论】:

【参考方案3】:

另一种方法是根本不使用 async,而只是递归地返回一个 Promise:

const getItems = (url) =>
  axios(
    baseURL: url,
    method: 'get',
  ).then((response) => response.data);

const getData = (initialUrl) => 
  const recur = (result, nextUrl) =>
    !nextUrl
      ? Promise.resolve(result)
      : getItems(nextUrl).then((data) =>
          recur(result.concat([data.results]), data.next),
        );
  return recur([],initialUrl)
    .catch(e=>Promise.reject(e.stack));//reject with error stack
;

正如CertainPerformance 指出的那样;您不需要在每个级别都捕获,如果您希望 getData 使用 error.stack 拒绝,您只需捕获一次。

但是;如果您有 100 个下一个 url,其中 99 个很好,但只有最后一个失败了,您想以一种保持结果的方式拒绝,以便您可以再试一次?

如果你这样做了,那么代码可能看起来像这样:

const getData = (initialUrl) => 
  const recur = (result, nextUrl) =>
    !nextUrl
      ? Promise.resolve(result)
      : getItems(nextUrl)
        .catch(e=>Promise.reject([e,result]))//reject with error and result so far
        .then((data) =>
          recur(result.concat([data.results]), data.next),
        );
  return recur([],initialUrl);//do not catch here, just let it reject with error and result
;

【讨论】:

以上是关于async/await 函数中的 JavaScript Promise 解析最终响应数组的主要内容,如果未能解决你的问题,请参考以下文章

async/await 函数中的 JavaScript Promise 解析最终响应数组

javascript 中的 async/await 示例

promise 和 async await比较

从 Nodejs 中的 mySQL 获取数据时如何从 Async/await 函数获取返回值

使用带有 async/await 的 mongoose 承诺

使用 async/await 进行续集查询的无限循环