Promise使用笔记

Promise使用笔记

October 14, 2016
NodeJs Promise

环境描述:nodejs Promise模块:q

1. 多层异步Promise的实现

需求:获取一个目录下指定文件(多个)的数据集合 从错误思路到正确思路进行分析, 解析错误思路是为了知己知彼,为了不在写出这种错误的代码,而且错误的思路中不是全部都错误的,是在某个分界点才开始错误的,所以这个分界点是个关键,需要多加注意

1.1 错误思路

先直接上完整代码

var jsonParser = function(basePath) {
    var deferred = q.defer();
    var resultData = [];
    try {
        fs.readdir(basePath, function(err, files) {
            if (err) {
                console.error(err);
                deferred.reject(err);
            }
            if (files.length < 1) {
                deferred.resolve(resultData);
            }
            for (var i = 0, n = files.length; i < n; i++) {
                var file = files[i];
                var fileExtIndex = file.indexOf(".json");
                if (fileExtIndex > -1) {
                    fs.readFile(path.join(basePath, file), function(err, data) {
                        if (err) {
                            console.error(err);
                            deferred.reject(err);
                        }
                        try {
                            var jsonData = JSON.parse(data);
                            resultData[resultData.length] = jsonData;
                            if (i >= n - 1) {
                                deferred.resolve(resultData);
                            }
                        } catch (e) {
                            console.log(e);
                            deferred.reject(e);
                        }
                    })
                }
            }
        });
    } catch (e) {
        deferred.reject(e)
    }
    return deferred.promise;
}
var deferred = q.defer();
return deferred.promse;
try {
    fs.readdir(basePath, function(err, files) {})
} catch (e) {
    deferred.reject(e)
}
if (err) {
    console.error(err);
    deferred.reject(err);
}
if (files.length < 1) {
    deferred.resolve(resultData);
}
for (var i = 0, n = files.length; i < n; i++) {
    fs.readFile(path.join(basePath, file), function(err, data) {}
}
if (err) {
    deferred.reject(err);
}
try {
    var jsonData = JSON.parse(data);
    ...
} catch (e) {
    deferred.reject(e);
}
resultData[resultData.length] = jsonData;
if (i >= n - 1) { // i == n-1
    deferred.resolve(resultData);
}

下面是回过头去在想想不对劲的地方和代码根本没法写的地方

var fileExtIndex = file.indexOf(".json");
if (fileExtIndex > -1) {
    //readfile
} else {
    
}
fs.readFile(path.join(basePath, file), function(err, data) {}
for (var i = 0, n = files.length; i < n; i++) {
    fs.readFile(path.join(basePath, file), function(err, data) {}
}

总结:通过上面的分析会发现根本的问题就是出现在循环处理fs.readFile这个异步方法,另外可能还有有其他想法就是,打算一个一个文件的进行读取,这个好知道啥时候读到最后的文件(可能可以实现,但是仍然不可取)

1.2 正确思路

同理,先上完整代码

function readFiles(basePath, files) {
    var promises = [];
    for (var i = 0, n = files.length; i < n; i++) {
        var file = files[i];
        if (file.indexOf(".json") > -1) { //必须是.json的扩展文件
            promises.push(q.Promise(function(resolve, reject, notify) {
                fs.readFile(path.join(basePath, file), function(err, data) {
                    if (err) {
                        reject(err);
                    }
                    try {
                        resolve(JSON.parse(data));
                    } catch (e) {
                        reject(e);
                    }
                })
            }));
        }
    }
    return q.all(promises);
}

//获取目录下面所有文件,并解析为json格式的数组
var jsonParser = function(basePath) {
    var deferred = q.defer();
    try {
        fs.readdir(basePath, function(err, files) {
            if (err) {
                deferred.reject(err);
            }
            if (files.length < 1) {
                deferred.resolve([]);
            }
            readFiles(basePath, files).then(function(data) {
                deferred.resolve(data)
            }, function(error) {
                deferred.reject(data)
            })
        });
    } catch (e) {
        deferred.reject(e)
    }
    return deferred.promise;
}

会发现重大改变的地方就是我们分析的第4步开始改变的

readFiles(basePath, files).then(function(data) {
    deferred.resolve(data)
}, function(error) {
    deferred.reject(data)
})

这个会发现很爽,我不管结果如何,我就是监听then的结果,进行resolve或者reject 核心代码:

function readFiles(basePath, files) {
    var promises = [];
    for (var i = 0, n = files.length; i < n; i++) {
        var file = files[i];
        if (file.indexOf(".json") > -1) { //必须是.json的扩展文件
            promises.push(q.Promise(function(resolve, reject, notify) {
                fs.readFile(path.join(basePath, file), function(err, data) {
                    if (err) {
                        reject(err);
                    }
                    try {
                        resolve(JSON.parse(data));
                    } catch (e) {
                        reject(e);
                    }
                })
            }));
        }
    }
    return q.all(promises);
}

分析:这不是单纯的进行的代码的封装,重点是返回的q.all(promises),代码中把所有的fs.readFile异步处理封装成Promise对象,用q.all()等待机制,等待所有的文件读取好以后统一进行返回,会发现这个更加严谨,对个各个角度的都有响应的resolvereject,而不会导致卡死问题