这些年来,我创建了一个在项目中使用的标准方法库。他们中的许多人都有回调。我最近了解了Promise和async / await,以使代码读取同步并在早期的回调中喜欢它。
对于正在进行的项目和将来的项目,我想对库中的旧方法使用async / await。是否可以简单地在它们前面添加“异步”,然后在我调用它们时添加“等待”以使它们承诺友好。但是同时我的较旧项目可以使用它们并将它们称为回调吗?
例如:
var checkIfFileExists = function(filename,callback){
fs.stat(filename, function(err, stat) {
if(err == null) {
let jsonObj = { 'status' : true,
'error' : false
};
} else if(err.code == 'ENOENT') {
console.log( 'File ' + filename + ' does not exists');
let jsonObj = { 'status' : false,
'error' : err
};
} else {
let jsonObj = { 'status' : false,
'error' : err
};
}
callback(jsonObj);
});
};
对于这种方法,我可以像这样简单地添加'async':
var checkIfFileExists = async function(filename,callback){ .... }
然后我的旧项目将其称为回调:
checkIfFileExists(fileToCheck, function(jsonResponse){
// do something with response
});
,新项目将其称为:
let jsonResponse = await checkIfFileExists(fileToCheck);
答案 0 :(得分:2)
通过基本包装使用标准回调参数的现有函数以返回诺言的方式,存在各种用于“承诺”库。这可能对您有用。
例如:https://www.npmjs.com/package/es6-promisify
const {promisify} = require("es6-promisify");
const checkIfFileExistsPromise = promisify(checkFileIfExists);
const exists = await checkIfFileExistsPromise(...);
还要注意,fs
如今已经在Node.js中内置了基于承诺的API:https://nodejs.org/api/fs.html#fs_fs_promises_api
答案 1 :(得分:1)
首先,即使是较旧的代码也可以使用promises。几乎可以在任何版本的node.js或浏览器中轻松填充承诺,并且调用方可以很好地使用.then()
和.catch()
。因此,我的第一个论点是,该是时候向前迈进,而将旧的回调样式留在后面了。使用承诺不会阻止任何人使用您的代码,只会迫使他们将其编程知识转移到正确的十年中。
如果您确实想在同一接口中同时提供回调和Promise选项,则不能仅将async
放在函数上并使所有功能正常工作。这不是async
所做的。它没有那种超级能力。另外,如果您想与旧版本的node.js兼容,那么该版本也可能不支持async
。
制作可以调用的任何一种方法的通常方法是检测是否通过了回调,并且代码根据是否通过了回调进行调整,如果满足则返回与代码完成相关的promise没有回调传递,如果传递了回调,则使用回调。
因此,如果您有一个接口checkIfFileExists(...)
,则可以这样使用它:
// with callback
checkIfFileExists("myfile.txt", function(err, exists) {
if (err) {
console.log(err);
} else {
console.log(exists);
}
});
// with promise
checkIfFileExists("myfile.txt").then(function(exists) {
console.log(exists);
}).catch(function(err) {
console.log(err);
});
而且,这是一个实现:
const fs = require('fs');
const promisify = require('util').promisify;
function makePromisify(fn) {
if (fn._p) {
return fn._p;
} else {
fn._p = promisify(fn);
return fn._p;
}
}
// if not called with callback, then returns a promise
function checkIfFileExists(filename, callback) {
if (!callback) {
return makePromisify(checkIfFileExists)(filename);
} else {
fs.stat(filename, function(err, stats) {
if (err) {
if (err.code === 'ENOENT') {
callback(null, false);
} else {
callback(err);
}
} else {
callback(null, true);
}
});
}
}
此实现使用util.promisify()
(在节点v8.0中添加)自动为您创建了回调接口的承诺版本。如果要支持足够老的版本{.1}甚至不存在的node.js,那么也可以用几行代码手动构建。
出于效率方面的考虑,它在首次使用时将已承诺的功能版本缓存为被调用函数的util.promisify()
属性,因此在后续调用中,它可以使用与该功能完全相同的承诺版本。
请注意,我希望先对Promise接口进行优化的设计,因为这是更可能的应用,因为这是Java语言的未来,应该是更常见的用法。但是,对于这样的功能,您需要使用._p
模块的fs.promises
接口,并且至少假定节点v10.0。
如果您可以假设使用最新的节点v10,则可以使用fs
界面来简化此操作,并且可以更简化诺言用法:
fs.promises