在继续之前如何等待递归函数完全完成?
How can I wait for a recursive function to completely finish before continuing?
我有一个看起来像这样的函数:
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 行)。
它所做的只是循环遍历嵌套对象以获取末尾的值,并将它们添加到 url 字符串中。
let urlParameters = await convertExamples(data,'gui','?');
console.log('finished url parameters:',urlParameters);
目前它似乎不起作用,因为 urlParameters 仍然等于 ?最后。
我怎样才能让程序在继续之前等待 convertExamples()
函数完成(包括来自其自身的每个实例 运行)?
我尝试在调用它之前添加 await
,但似乎没有任何改变。
如果可能的话,我宁愿避免承诺。
您对异步编程和递归函数之间的区别感到困惑。您需要“等待”异步进程以承诺解决、回调或 async/await 完成。但是这个函数是同步的,尽管它是递归的。你没有得到你期望的答案,因为你没有返回任何值。您无需执行任何额外操作即可等待它完成。获取“?”的输出表示完成。
这是您的函数的更正版本。它在递归时跟踪路径,但不需要第三个参数。
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&
编辑
那有什么区别呢?您的递归函数使 JavaScript 进程一直处于忙碌状态。在这方面,调用自身的函数与相互调用的不同函数链没有什么不同。 JavaScript 在所有这些执行完成之前不会也不能做任何其他事情。那是同步的。
异步编程是指 JavaScript 注册回调(即要执行的代码)以在将来由事件触发,然后 停止 。您正在考虑的“等待”实际上是关于“我如何确保 JavaScript 在该事件发生时醒来并再次开始执行”?该事件可能是网络呼叫返回,或者用户单击他们的鼠标,或者您设置的最终与时钟匹配的超时。正如我之前暗示的那样,回调、承诺和 async/await 都只是关于我们如何在未来某个事件中将代码排队到 运行 的糖分。
字符串在 javascript 中是原始的和不可变的,因此您不能修改它们 - 只能替换。 urlParameters += ...
行实际上创建了 urlParameters
的本地副本并对其进行处理,而不是修改原始副本。您可以 return 来自每个函数调用的字符串:
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 的 post 告诉您这可以是一个纯同步函数。我觉得你应该更进一步,把程序的关注点分开。
我们可以从对象扁平化功能开始,flatten
。这允许我们以线性方式处理任意嵌套的对象。最重要的是,它是通用的,这意味着它可以在您需要此行为的任何时候重复使用 -
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
但它适合他们的需要。我们将使用 URLSearchParams,而不是手动使用字符串连接将 URL 个组件组合在一起。这是一个更安全的 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
我有一个看起来像这样的函数:
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 行)。
它所做的只是循环遍历嵌套对象以获取末尾的值,并将它们添加到 url 字符串中。
let urlParameters = await convertExamples(data,'gui','?');
console.log('finished url parameters:',urlParameters);
目前它似乎不起作用,因为 urlParameters 仍然等于 ?最后。
我怎样才能让程序在继续之前等待 convertExamples()
函数完成(包括来自其自身的每个实例 运行)?
我尝试在调用它之前添加 await
,但似乎没有任何改变。
如果可能的话,我宁愿避免承诺。
您对异步编程和递归函数之间的区别感到困惑。您需要“等待”异步进程以承诺解决、回调或 async/await 完成。但是这个函数是同步的,尽管它是递归的。你没有得到你期望的答案,因为你没有返回任何值。您无需执行任何额外操作即可等待它完成。获取“?”的输出表示完成。
这是您的函数的更正版本。它在递归时跟踪路径,但不需要第三个参数。
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&
编辑
那有什么区别呢?您的递归函数使 JavaScript 进程一直处于忙碌状态。在这方面,调用自身的函数与相互调用的不同函数链没有什么不同。 JavaScript 在所有这些执行完成之前不会也不能做任何其他事情。那是同步的。
异步编程是指 JavaScript 注册回调(即要执行的代码)以在将来由事件触发,然后 停止 。您正在考虑的“等待”实际上是关于“我如何确保 JavaScript 在该事件发生时醒来并再次开始执行”?该事件可能是网络呼叫返回,或者用户单击他们的鼠标,或者您设置的最终与时钟匹配的超时。正如我之前暗示的那样,回调、承诺和 async/await 都只是关于我们如何在未来某个事件中将代码排队到 运行 的糖分。
字符串在 javascript 中是原始的和不可变的,因此您不能修改它们 - 只能替换。 urlParameters += ...
行实际上创建了 urlParameters
的本地副本并对其进行处理,而不是修改原始副本。您可以 return 来自每个函数调用的字符串:
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 的 post 告诉您这可以是一个纯同步函数。我觉得你应该更进一步,把程序的关注点分开。
我们可以从对象扁平化功能开始,flatten
。这允许我们以线性方式处理任意嵌套的对象。最重要的是,它是通用的,这意味着它可以在您需要此行为的任何时候重复使用 -
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
但它适合他们的需要。我们将使用 URLSearchParams,而不是手动使用字符串连接将 URL 个组件组合在一起。这是一个更安全的 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