返回promise

时间:2017-01-10 09:50:24

标签: javascript recursion promise apache-zookeeper

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

2 个答案:

答案 0 :(得分:1)

有两件事是错的......如果孩子不是空的,你永远不会解决......而你的孩子。地图也可能是你每次使用它的方式

所以,首先,如果孩子有一个长度,你想要解决一些事情,其次,你只想在所有孩子的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是否真实

答案 1 :(得分:1)

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

由于promises只解析为单个值,因此在将多个值传递给其回调的内容时,必须将每个值变为对象并使用该对象解析。

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

getInitialTimeStamp()返回一个promise,该promise将解析为包含所需路径属性的对象。

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