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

What am I doing wrong in this recursive function, when returning promise

我需要获取 zookeeper 中所有 znodes 的初始 timestamps。我正在使用 node-zookeeper-client 的 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() 部分转换为 运行。我知道这与返回承诺有关,但我不知道这里做错了什么。考虑到我在 promises 和 async 编程方面缺乏知识,并为我提供解决方案。

有两处错误....如果children不为空,你永远不会解决...而且你的children.map也可能是你使用它的方式的forEach

所以,首先,如果 children 有长度,你想解决一些问题,其次,你只想在 children 的所有 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);
        });
    });
}

虽然可以稍微清理一下

function getInitialTimeStamp(client, path) {
    return new Promise((resolve, reject) => {
        client.getChildren(path, (error, children, stats) => {
            timeStamp[path]= {ctime: stats.ctime, mtime: stats.mtime};  
            resolve(Promise.all((children||[]).map(child => getInitialTimeStamp(client, path + '/' + child))));
        });
    });
}

但仍然没有进行错误检查...即测试 error 是否为真

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

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

此外,您的实施似乎正在修改某些 timeStamp 全局或更高范围的变量,这似乎不太理想。所以,我已经这样做了,所以你可以选择传入一个对象作为开始,但如果你不这样做,它将默认为一个空对象,并且在任何一种情况下,该函数都会 return 一个将解析为填充有所需属性的对象,包括 cnt 属性 这样您就可以更轻松地查看其中有多少。

getInitialTimeStamp()return是一个解析为包含所需路径属性的对象的承诺。

// 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;
        });
    });
}

用法:

getInitialTimeStamp(client, somePath).then(resultsObj => {
     // process resultsObj here
}).catch(err => {
    // process error here
});