繁体   English   中英

如何在继续之前等待递归 function 完全完成?

[英]How can I wait for a recursive function to completely finish before continuing?

我有一个看起来像这样的 function:

async function convertExamples(data,path,urlParameters) {
    urlParameters='h'
    for (var k in data) {
        if (typeof data[k] == "object" && data[k] !== null)
            return await convertExamples(data[k], path+'.'+k, urlParameters); 
        else {
            return urlParameters + path+'.'+k + '=' + data[k] + '&';
        }
    }
    return urlParameters;
}

如您所见,它是递归的(它在第 4 行自行运行)。

它所做的只是遍历嵌套的 object 以获取最后的值,并将它们添加到 url 字符串中。

let urlParameters = await convertExamples(data,'gui','?');
console.log('finished url parameters:',urlParameters);

现在它似乎不起作用,因为 urlParameters 仍然等于? 在最后。

我怎样才能让程序等待convertExamples() function 完成(包括从自身内部运行的每个实例),然后再继续?

我尝试在调用它之前添加await ,但似乎没有任何改变。

如果可能的话,我宁愿避免承诺。

您对异步编程和递归函数之间的区别感到困惑。 您需要“等待”异步进程完成 promise 分辨率、回调或异步/等待。 但是这个 function 是同步的,尽管是递归的。 你没有得到你期望的答案,因为你没有返回任何值。 你不需要做任何额外的事情来等待它完成。 获取“?”的 output 意味着它已经完成了。

这是您的 function 的更正版本。 它在递归时跟踪路径,但不需要第三个参数。

function convertExamples(data, path)   {
    let urlParameters = '';
    for (var k in data) {
        if (typeof data[k] == "object" && data[k] !== null)
            urlParameters += convertExamples(data[k], path+'.'+k);
        else
            urlParameters += path+'.'+k + '=' + data[k] + '&';
    }
    return urlParameters;
}

const data = {
  a: {
    b: 'foo',
    c: 'bar',
  },
  d: 'baz',
};

const result = '?' + convertExamples(data,'gui')

console.log('finished url parameters:',result);

// finished url parameters: ?gui.a.b=foo&gui.a.c=bar&gui.d=baz&


编辑

那么有什么区别呢? 您的递归 function 使 JavaScript 进程一直忙。 在这方面,调用自身的 function 与相互调用的不同函数链没有什么不同。 在所有这些执行完成之前,JavaScript 不会也不能做任何其他事情。 那是同步的。

异步编程是当 JavaScript 注册一个回调(即要执行的代码)以由将来的事件触发然后停止 您正在考虑的“等待”实际上是“我如何确保 JavaScript 在该事件发生时唤醒并再次开始执行”? 该事件可能是网络调用返回,或者用户单击鼠标,或者您设置的最终与时钟匹配的超时。 正如我之前所暗示的,回调、promise 和 async/await 都只是我们如何排队代码以便将来在某些事件上运行的糖。

字符串在 javascript 中是原始且不可变的,因此您无法修改它们 - 只能替换。 urlParameters +=...行实际上创建了urlParameters的本地副本并对其进行处理,而不是修改原始文件。 您可以从每个 function 调用中返回字符串:

function convertExamples(data,path,urlParameters)   {
    for (var k in data) {
        if (typeof data[k] == "object" && data[k] !== null)
            urlParameters = convertExamples(data[k], path+'.'+k, urlParameters); 
        else 
            urlParameters += path+'.'+k + '=' + data[k] + '&';
    }
    return urlParameters;
}
...
let urlParameters = '?';
urlParameters = convertExamples(data,'gui',urlParameters)

或者更好的是,使用可修改的数据类型,例如数组,并在最后将其解析为字符串:

function convertExamples(data,path,paramsArr)   {
    for (var k in data) {
        if (typeof data[k] == "object" && data[k] !== null)
            convertExamples(data[k], path+'.'+k, paramsArr); 
        else 
            paramsArr.push(path+'.'+k + '=' + data[k]);
    }
}
...
const paramsArr = [];
convertExamples(data,'gui',paramsArr);
const urlParameters = '?' + paramsArr.join('&');

Chris的帖子教你,这可以是纯同步的function。 我认为您应该进一步 go 并分离程序的关注点。

我们可以从 object 开始展平 function, flatten 这允许我们以线性方式使用任意嵌套的 object。 最重要的是,它是通用的,这意味着它可以在您需要此行为的任何时候重用 -

 function* flatten (t, path = []) { switch (t?.constructor) { case Object: case Array: for (const [k,v] of Object.entries(t)) yield *flatten(v, [...path, k]) break default: yield [path, t] } } const data = { a: { b: 'foo', c: 'bar', }, d: 'baz', }; for (const [path, value] of flatten(data)) console.log(JSON.stringify(path), value)

["a","b"] foo
["a","c"] bar
["d"] baz

接下来我们将编写objectToParams ,它是我们flatten的简单包装器。 请注意调用者如何处理适合他们需要的path 而不是使用字符串连接手动将 URL 组件组合在一起,我们将使用URLSearchParams 这是一个更安全的 API 并直接与 URL api 联系在一起 -

function objectToParams (t, base)
{ const p = new URLSearchParams()
  for (const [path, value] of flatten(t, base ? [ base ] : []))
    p.set(path.join("."), value)
  return p.toString()
}

console.log(objectToParams(data))
console.log(objectToParams(data, "gui"))

展开下面的代码片段以在您自己的浏览器中验证结果 -

 function* flatten (t, path = []) { switch (t?.constructor) { case Object: case Array: for (const [k,v] of Object.entries(t)) yield *flatten(t[k], [...path, k]) break default: yield [path, t] } } function objectToParams (t, base) { const p = new URLSearchParams() for (const [path, value] of flatten(t, base? [ base ]: [])) p.set(path.join("."), value) return p.toString() } const data = { a: { b: 'foo', c: 'bar', }, d: 'baz', }; console.log(objectToParams(data)) console.log(objectToParams(data, "gui"))

a.b=foo&a.c=bar&d=baz
gui.a.b=foo&gui.a.c=bar&gui.d=baz

暂无
暂无

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

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