簡體   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