繁体   English   中英

为什么这个递归函数不异步运行?

[英]Why does this recursive function not run asynchronously?

我有一个start(node, array)函数,它应该通过通过callMsGraph(token, end)对 API 的递归调用遍历对象树来执行 DFS,直到在树的末尾找到image属性,此时它们是推送到array 该函数似乎可以正常工作,但除非我将其包装在 2 秒setTimeout中,否则我无法获得output ,这表明没有等待递归完成。 我想更多地使用 async/await,但它不是顶级的。

我不确定nextNode.then是否正在做任何事情,或者可能需要等待callMsGraph()与我所知道的不同。 一个解决方案将不胜感激。

shelfdb.data = async (accessToken) => {

    const token = accessToken;
    const endpoint = 'https://graph.microsoft.com/v1.0/sites/webgroup.sharepoint.com,23e7ef7a-a529-4dde-81ba-67afb4f44401,0fa8e0f7-1c76-4ad0-9b6e-a485f9bfd63c/drive/items/01GNYB5KPQ57RHLPZCJFE2QMVKT5U3NYY3/children'
    
    function start(node, array) {
        if(node.value.length > 0) {
            node.value.forEach(function(child) {
                var end = 'https://graph.microsoft.com/v1.0/sites/webgroup.sharepoint.com,23e7ef7a-a529-4dde-81ba-67afb4f44401,0fa8e0f7-1c76-4ad0-9b6e-a485f9bfd63c/drive/items/' + child.id + '/children';
                var nextNode = callMsGraph(token, end);
    
                nextNode.then(function(currResult) {
                    if (currResult.value.length > 0) {

                        if ('image' in currResult.value[0]) {
                            currResult.value.forEach(function(imgChild) {
                                let img = {
                                    'name': imgChild.name,
                                    'job': imgChild.parentReference.path.split("/")[6],
                                    'path': imgChild.webUrl,
                                    'id': imgChild.id
                                }
                                array.push(img);
                            })
                            // complete storing images at tail object, go one level up after loop
                            return;
                        }

                        // if no 'image' or value, go into child
                        start(currResult, array);
                    }
                }).catch(function(e) {
                    console.error(e.message);
                })
            })
        }
    
        return array;
    }
    
    
    var res = await callMsGraph(token, endpoint); // start recursion
    var output = start(res, []); 
    console.log(output); // only displays value if wrapped in setTimeout

    return output; // empty []

}

通过callMsGraph()对 API 的每个查询都会返回一个像这样的对象,其中使用每个对象/文件夹的id (作为新端点)的value进行后续查询,直到找到具有image属性的对象。 MS Graph API 要求在每个级别展开文件夹以访问其子级。

{
  id: '01GNYB5KPQ57RHLPZCJFE2QMVKT5U3NYY3'
  value: [
    {
      id: '01GNYB5KJMH5T4GXADUVFZRSITWZWNQROS',
      name: 'Folder1',
    },
    {
      id: '01GNYB5KMJKILOFDZ6PZBZYMXY4BGOI463',
      name: 'Folder2',
    }
  ]
}

这是 callMsGraph() 助手:

function callMsGraph(accessToken, graphEndpoint) {
    const headers = new Headers();
    const bearer = `Bearer ${accessToken}`;

    headers.append("Authorization", bearer);

    const options = {
        method: "GET",
        headers: headers
    };

    return fetch(graphEndpoint, options)
        .then(response => response.json())
        .catch(error => {
            console.log(error);
            throw error;
        });
}

Promise 的规则是,一旦你选择了一个(更有可能是被库强制进入它),所有需要在任何地方阻止结果的代码也必须等待。 无法“返回”同步,并且即使在承诺开始的位置和您想要其结果的位置之间的承诺链中的单个片段都没有等待,结果将无法到达*

取一段代码:

function start(node, array) { // not async!
    // ..
    node.value.forEach(function(child) { // doesn't await!
        // ..
        nextNode.then(function(currResult) {
        // this promise is not hooked up to anything!
        start(...) // recurse without await!

then前面没有awaitstart不返回 Promise 也不递归地等待, forEach无法等待其回调的异步结果,因此nextNode.then链中的每个 Promise 都将永远孤立到 void * .

解决方案是这样的结构:

async function start(node, array) {
    // ..
    for (const child of node.value) {
        // ..
        const currResult = await callMsGraph(token, end);
        // ..
        await start(...);
        array.push(currResult);
    }
    // returns a promise implicitly
}

// ..
await start(...);
// `array` is populated here

或者Promise.all ,它并行运行并返回一个数组(可以替换参数array ):

function start(node, array) {
    return Promise.all(node.value.map(async child => {
        const currResult = await callMsGraph(token, end);
        // ..
        await start(...);
        return currResult;
    }));
}

我很乐意提供一个最小的、可运行的示例,但是您提供的代码不可运行,因此您必须稍微调整一下才能为您工作。 如果您确保await所有内容,那么您就可以开始了(并且通常避免混合.thenasync / await ——后者对于这个用例来说似乎更容易)。

*(出于所有实际目的和目的)

有几个地方你没有处理代码中返回的承诺。 nextNode.then如果您的 forEach 循环只是“调用”,则下一行代码不会等待它完成,forEach 循环将在调用回调then完成执行。

我稍微更改了您的代码,但我无法检查它是否正常工作,因为我需要为callMsGraph填充虚拟数据,但如果您遇到任何问题 - 告诉我,我会修改答案

shelfdb.data = async (accessToken) => {
  const token = accessToken;
  const endpoint = 'https://graph.microsoft.com/v1.0/sites/webgroup.sharepoint.com,23e7ef7a-a529-4dde-81ba-67afb4f44401,0fa8e0f7-1c76-4ad0-9b6e-a485f9bfd63c/drive/items/01GNYB5KPQ57RHLPZCJFE2QMVKT5U3NYY3/children'

  const images = [];

  async function start(node, array) {
    if (node.value.length <= 0) return array; // or === 0 or whatever

    for (const child of node.value) {
      const end = `https://graph.microsoft.com/v1.0/sites/webgroup.sharepoint.com,23e7ef7a-a529-4dde-81ba-67afb4f44401,0fa8e0f7-1c76-4ad0-9b6e-a485f9bfd63c/drive/items/${child.id}/children`;
      const nextNode = await callMsGraph(token, end);
      if (nextNode.value.length > 0) {
        if ('image' in nextNode.value[0]) {
          const mapped = nextNode.value.map(imgChild => {
            return {
              'name': imgChild.name,
              'job': imgChild.parentReference.path.split("/")[6],
              'path': imgChild.webUrl,
              'id': imgChild.id
            }
          });
          array.push(...mapped);
        }

        // if no 'image' or value, go into child
        await start(nextNode, array);
      }
    }

    return array;
  }


  var res = await callMsGraph(token, endpoint);
  var output = await start(res, []);
  console.log(output);

  return output;
}

另外,请随时在您需要的任何地方添加 try{} catch{} 块,我跳过了它们

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM