异步递归函数

问题描述 投票:0回答:1

我正在尝试创建一个节点js alexa app来控制我的本地kodi实例。我使用Node-Kodi作为JsonRPC API的包装器。在整个应用程序中,我一直在使用Async / await而没有任何问题。我的代码可以在我的公共回购中看到here

当我尝试执行递归异步功能时,我遇到了问题。

app.intent("AddonInfoTest",
    {
        "slots": {"ADDON": "ADDON_SLOT"},
        "utterances": ["get info for +ADDON+"]
    },
    async function(request, response) {
        let addon = await kodi.addons.getAddonDetails({
            'addonid': 'plugin.video.youtube', 
            'properties': ['path']
        });

        console.log(addon.addon.path);

        let addonMenu = await kodi.files.getDirectory('plugin://plugin.video.youtube');

        let menu = buildAddonMenu(addonMenu);

        console.log('menu: ' + JSON.stringify(menu));  
    }
);

function buildAddonMenu(menu, depth = 0)
{
    var addonMenu = {};
    var subMenu = {};

    menu.files.forEach(async function(file) {
        addonMenu[file.label] = {
            'link': file.file
        }

        console.log(file);
        console.log(depth)

        if (file.filetype == 'directory' && !file.label.match(/^next page.*/i) && depth <= constants.addon_menu_max_depth) {
            try {
                let subMenu = await kodi.files.getDirectory(file.file);
                if (typeof subMenu !== 'undefined' && 'files' in subMenu) {
                    console.log('Building Sub Menu for ' + file.label);
                    // console.log('SubMenu: ' + JSON.stringify(subMenu));
                    addonMenu[file.label]['sub_menu'] = buildAddonMenu(subMenu, depth +1);
                }
            } catch (error) {
                console.error(error);
            }
        }
    });

    return addonMenu;
}

似乎正在发生的事情是,我只在第一级后得到一个返回值,并且该函数没有等待构建子菜单。

使用youtube插件作为示例,控制台输出如下所示:

[2018-01-02T22:49:58.435Z] { file: 'plugin://plugin.video.youtube/sign/in/',
  filetype: 'directory',
  label: '[B]Sign In[/B]',
  type: 'unknown' }
[2018-01-02T22:49:58.436Z] 0
[2018-01-02T22:49:58.437Z] { file: 'plugin://plugin.video.youtube/special/popular_right_now/',
  filetype: 'directory',
  label: 'Popular right now',
  type: 'unknown' }
[2018-01-02T22:49:58.437Z] 0
[2018-01-02T22:49:58.438Z] { file: 'plugin://plugin.video.youtube/kodion/search/list/',
  filetype: 'directory',
  label: 'Search',
  type: 'unknown' }
[2018-01-02T22:49:58.438Z] 0
[2018-01-02T22:49:58.439Z] { file: 'plugin://plugin.video.youtube/special/live/',
  filetype: 'directory',
  label: 'Live',
  type: 'unknown' }
[2018-01-02T22:49:58.439Z] 0
[2018-01-02T22:49:58.442Z] { file: 'plugin://plugin.video.youtube/config/youtube/',
  filetype: 'directory',
  label: 'Settings',
  type: 'unknown' }
[2018-01-02T22:49:58.442Z] 0
[2018-01-02T22:49:58.442Z] menu: {"[B]Sign In[/B]":{"link":"plugin://plugin.video.youtube/sign/in/"},"Popular right now":{"link":"plugin://plugin.video.youtube/special/popular_right_now/"},"Search":{"link":"plugin://plugin.video.youtube/kodion/search/list/"},"Live":{"link":"plugin://plugin.video.youtube/special/live/"},"Settings":{"link":"plugin://plugin.video.youtube/config/youtube/"}}
[2018-01-02T22:49:59.171Z] Building Sub Menu for Search
[2018-01-02T22:49:59.171Z] { file: 'plugin://plugin.video.youtube/kodion/search/input/',
  filetype: 'directory',
  label: '[B]New Search[/B]',
  type: 'unknown' }
[2018-01-02T22:49:59.171Z] 1
[2018-01-02T22:49:59.171Z] { file: 'plugin://plugin.video.youtube/kodion/search/query/?q=minecraft',
  filetype: 'directory',
  label: 'minecraft',
  type: 'unknown' }
[2018-01-02T22:49:59.171Z] 1
[2018-01-02T22:49:59.172Z] { file: 'plugin://plugin.video.youtube/kodion/search/query/?q=Hello+World',
  filetype: 'directory',
  label: 'Hello World',
  type: 'unknown' }
[2018-01-02T22:49:59.172Z] 1
[2018-01-02T22:49:59.172Z] { file: 'plugin://plugin.video.youtube/kodion/search/query/?q=hello+world',
  filetype: 'directory',
  label: 'hello world',
  type: 'unknown' }

我不是JavaScript而是PHP开发人员,所以我很可能没有正确使用异步函数。请有人指出我正确的方向。

完整的代码可以在这个Branch找到

javascript asynchronous async-await
1个回答
1
投票

buildAddonMenu需要是一个async函数,并使用await运算符调用。虽然菜单可能会继续以异步方式构建(因为buildAddonMenu会返回一个可以在以后更新的对象),但在同步调用buildAddonMenu后尝试立即渲染或检查菜单会失败 - 您只能获得由提供的数据生成的第一级菜单项在同步通话中。

所以每次打电话给buildAddonMenu都应该采取这种形式

 menuObject = await buildAddonMenu(....)

包括在app.Intent电话中的那个。

在对上面的控件声明进行更改之后

 menu.files.forEach(async function(file) {...}

显然想要为目录中的每个文件执行异步函数。提供的匿名函数确实需要是async才能在函数体中使用await - 但forEach不会等待它对异步函数完成的调用。

Using async/await with a forEach loop有多个解决方案来等待异步调用在循环中完成。如果顺序添加完成的子菜单是可以接受的,那么最简单(和接受)的解决方案可能是创建一个实际的for循环,它等待处理await条目的循环体内的异步函数调用(使用menu.files)。

一般来说,for( ... in ...)循环返回可枚举的继承属性,不建议通过对象的本地属性进行迭代。使用for( ... of ...)甚至是标准的for(...;...;...)循环。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.