Javascript 在这个递归函数中,当返回承诺时,我做错了什么

Javascript 在这个递归函数中,当返回承诺时,我做错了什么,javascript,recursion,promise,apache-zookeeper,Javascript,Recursion,Promise,Apache Zookeeper,我需要在zookeeper中获取所有znode的初始时间戳。我正在使用NodeZooKeeper客户端的getChildren方法来实现这一点。我递归调用我的getInitialTimeStamp沿路径遍历。我的 函数看起来像这样 function getInitialTimeStamp(client,path){ return new Promise((resolve,reject) => { client.getChildren( pat

我需要在
zookeeper
中获取所有
znode
的初始
时间戳。我正在使用NodeZooKeeper客户端的
getChildren
方法来实现这一点。我递归调用我的
getInitialTimeStamp
沿路径遍历。我的 函数看起来像这样

function getInitialTimeStamp(client,path){
    return new Promise((resolve,reject) => {
        client.getChildren(
            path,
            function(error,children,stats){
                //if children empty, return
                if (typeof children === 'undefined' || children.length <= 0) {resolve();} 

                timeStamp[path]= {ctime: stats.ctime, mtime: stats.mtime};  
                children.map(child => {
                    getInitialTimeStamp(client,path+'/'+child);
                });
        });
    });
}
getInitialTimeStamp(client,path)
    .then(() => {
      console.log(timeStamp);
      console.log("finished");
});

问题是我无法运行
.then()
部件。我知道这与回报承诺有关,但我不知道这里做错了什么。考虑我在承诺和代码中缺少知识,ASYNC/<代码>编程,给我一个解决方案。

有两个错误。如果孩子不是空的,你永远不会解决。。。还有你的孩子们。地图也可能是你使用它的一种方式

因此,首先,如果子对象有一个长度,您需要解决一些问题,其次,您只需要在子对象的所有
getInitialTimeStamp
完成后,使用Promise.ALL来解决这些问题

function getInitialTimeStamp(client,path){
    return new Promise((resolve,reject) => {
        client.getChildren(
            path,
            function(error,children,stats){
                //if children empty, return
                if (typeof children === 'undefined' || children.length <= 0) {
                    resolve();
                } 
                timeStamp[path]= {ctime: stats.ctime, mtime: stats.mtime};  
                // use promise.all to wait for all child timestamps
                Promise.all(children.map(child => getInitialTimeStamp(client,path+'/'+child)))
                // and then resolve this path
                .then(resolve);
        });
    });
}

但仍然没有进行错误检查。。。i、 e.测试
错误
是否真实

有两件事出错。。。。如果孩子不是空的,你永远不会解决。。。还有你的孩子们。地图也可能是你使用它的一种方式

因此,首先,如果子对象有一个长度,您需要解决一些问题,其次,您只需要在子对象的所有
getInitialTimeStamp
完成后,使用Promise.ALL来解决这些问题

function getInitialTimeStamp(client,path){
    return new Promise((resolve,reject) => {
        client.getChildren(
            path,
            function(error,children,stats){
                //if children empty, return
                if (typeof children === 'undefined' || children.length <= 0) {
                    resolve();
                } 
                timeStamp[path]= {ctime: stats.ctime, mtime: stats.mtime};  
                // use promise.all to wait for all child timestamps
                Promise.all(children.map(child => getInitialTimeStamp(client,path+'/'+child)))
                // and then resolve this path
                .then(resolve);
        });
    });
}

但仍然没有进行错误检查。。。i、 e.测试
错误
是否真实我建议这种类型的实现,通过提示
client.getChildren()
在较低级别上提示。这使得使用承诺编写所有逻辑变得更加容易,并避免了JaramandaX实现中常见的陷阱,例如完全丢失错误处理和错误传播

由于承诺仅解析为单个值,因此当承诺将多个值传递给其回调时,必须将每个值硬塞进一个对象中,并使用该对象进行解析

此外,您的实现似乎正在修改某些
时间戳
全局或更高范围的变量,这似乎不太理想。因此,我这样做是为了让您可以有选择地传入一个要开始的对象,但如果您不这样做,它将默认为一个空对象,并且在任何一种情况下,函数都将返回一个承诺,该承诺将解析为填充了所需属性的对象,包括
cnt
属性,这样您就可以更容易地看到有多少属性

getInitialTimeStamp()
返回解析为包含所需路径属性的对象的承诺

// make promisified version that resolves to an object with properties on it
// Depending upon the situation, you might add this to the prototype rather than
// to an instance
client.getChildrenP = function(path) {
    return new Promise((resolve, reject) => {
        this.getChildren(path, (error, children, stats) => {
            if (error) return reject(error);
            resolve({children, stats});
        });
    });
}

// Returns a promise that resolves to a timeStamp object
// You can optionally pass in an object to be modified or that will default
// to an empty object.  In either case, the returned promise resolves to
// the finished object.
function getInitialTimeStamp(client, path, resultsObj){
    let obj = resultsObj || {cnt: 0};
    obj.cnt = obj.cnt || 0;
    return client.getChildrenP(path).then(results => {
        if (typeof results.children === 'undefined' || children.length <= 0) {
            // return results so far
            return obj;
        }
        ++obj.cnt;
        obj[path]= {ctime: results.stats.ctime, mtime: results.stats.mtime};  
        return Promise.all(children.map(child => {
            getInitialTimeStamp(client,path+'/'+child, obj);
        })).then(results => {
            return obj;
        });
    });
}

我建议这种类型的实现在较低的级别上通过promisising
client.getChildren()
来实现。这使得使用承诺编写所有逻辑变得更加容易,并避免了JaramandaX实现中常见的陷阱,例如完全丢失错误处理和错误传播

由于承诺仅解析为单个值,因此当承诺将多个值传递给其回调时,必须将每个值硬塞进一个对象中,并使用该对象进行解析

此外,您的实现似乎正在修改某些
时间戳
全局或更高范围的变量,这似乎不太理想。因此,我这样做是为了让您可以有选择地传入一个要开始的对象,但如果您不这样做,它将默认为一个空对象,并且在任何一种情况下,函数都将返回一个承诺,该承诺将解析为填充了所需属性的对象,包括
cnt
属性,这样您就可以更容易地看到有多少属性

getInitialTimeStamp()
返回解析为包含所需路径属性的对象的承诺

// make promisified version that resolves to an object with properties on it
// Depending upon the situation, you might add this to the prototype rather than
// to an instance
client.getChildrenP = function(path) {
    return new Promise((resolve, reject) => {
        this.getChildren(path, (error, children, stats) => {
            if (error) return reject(error);
            resolve({children, stats});
        });
    });
}

// Returns a promise that resolves to a timeStamp object
// You can optionally pass in an object to be modified or that will default
// to an empty object.  In either case, the returned promise resolves to
// the finished object.
function getInitialTimeStamp(client, path, resultsObj){
    let obj = resultsObj || {cnt: 0};
    obj.cnt = obj.cnt || 0;
    return client.getChildrenP(path).then(results => {
        if (typeof results.children === 'undefined' || children.length <= 0) {
            // return results so far
            return obj;
        }
        ++obj.cnt;
        obj[path]= {ctime: results.stats.ctime, mtime: results.stats.mtime};  
        return Promise.all(children.map(child => {
            getInitialTimeStamp(client,path+'/'+child, obj);
        })).then(results => {
            return obj;
        });
    });
}

如果直接发布
client.getChildren()
,这不是会简单得多吗。现在的答案是,如果你承诺较低的级别,然后使用承诺对所有逻辑进行编程,那么错误处理几乎是免费的。你如何承诺client.getChildren()?@Prasanna-我发布了我自己的答案,这两个答案都向你展示了如何承诺
client.getChildren()
独立运行,不依赖范围更大的
时间戳
变量,并提供完整的错误处理。如果直接向客户机.getChildren()
发出提示,这不是会简单得多吗。现在的答案是,如果你承诺较低的级别,然后使用承诺对所有逻辑进行编程,那么错误处理几乎是免费的。你如何承诺client.getChildren()?@Prasanna-我发布了我自己的答案,这两个答案都向你展示了如何承诺
client.getChildren()
独立运行,不依赖更大范围的
时间戳
变量,并提供完整的错误处理。我当时正在使用jfriend00,没有想到像这样的
结果,很好!我唯一关心的是调用
client.getChildrenP
中的
client.getChildrenP
-如果您有多个客户端对象(不管是什么!),那么看起来硬编码很混乱@JaromandaX-是的,我刚刚将
client.getChildren()
更改为
this.getChildren()
。这就是我的意思。在原始代码中,
obj[path]={ctime:results.stats.ctime,mtime:results.stats.mtime}也会被设置,我认为如果当前结果没有子项,则会跳过此设置-因为返回-我认为需要将子项检查逻辑移到下面this@JaromandaX-我认为这在OP的原始代码中不是正确的行为。如果它是有意的,这是一个奇怪的副作用,因为承诺已经解决了,但代码仍在继续做一些事情(包括再次尝试调用resolve)。我认为OP的意思是
retu