我是否可以通过使用promise将同步方法变为异步?
例如,同步读取文件(是fs.readFile
有回调):
// Synchronous read
var data = fs.readFileSync('input.txt');
我应该这样做:
function readFileAsync(){
return new Promise((resolve, reject) => {
try {
resolve(fs.readFileSync('input.txt'));
} catch(err) {
reject(err);
}
})
}
或使用async / await:
function async readFileAsync(){
try {
let result = await fs.readFileSync('input.txt');
return result;
} catch(err) {
return err;
}
})
}
答案 0 :(得分:5)
TL; DR NO,纯粹的同步功能不可以承诺,以避免阻塞
没有。对于一个可以实现的方法,它需要已经是异步的,即立即返回,并在完成时使用回调。
例如:
function loop1000() {
for (let i = 0; i < 1000; ++i) {}
}
不具有可承诺性,因为它不会立即返回,也不会使用回调。但
function loop1000(err, callback) {
process.nextTick(() => {
for (let i = 0; i < 1000; ++i) { }
callback();
});
}
是可以承诺的
function loop1000promisified() {
return new Promise((resolve, reject) => loop1000(resolve));
}
但 所有这些方法无论如何都要阻止循环。原始版本立即阻止,使用process.nextTick()
的版本将阻止下一个处理器滴答。使应用程序在循环期间无响应。
如果您想使loop1000()
异步友好,您可以将其重写为:
function loop1000(err, callback) {
const segmentDuration = 10;
const loopEnd = 1000;
let i = 0;
function computeSegment() {
for (let segment = 0;
segment < segmentDuration && i < loopEnd;
++segment, ++i) { }
if (i == loopEnd) {
callback();
return;
}
process.nextTick(computeSegment);
}
computeSegment();
}
因此,不是更长的阻塞时间,它会有几个较小的阻塞。然后,宣传的版本loop1000promisified()
可能会有所帮助。
免责声明:代码直接输入SO w / o任何测试。
答案 1 :(得分:1)
我是否可以通过使用promise将同步方法变为异步?
没有
我可以将同步方法变为异步吗?
没有。这就是为什么承诺在这里没有帮助的原因。您需要使用本机异步对应项,即fs.readFile
而不是fs.readFileSync
。
关于您的替代方案,您可能不应该这样做。但是如果你绝对需要一个同步函数来返回一个已履行或被拒绝的承诺(而不是抛出异常),你可以这样做
function readFileSync(){
return new Promise(resolve => {
resolve(fs.readFileSync('input.txt'))
});
}
或
async function readFileSync() {
return fs.readFileSync('input.txt');
}
答案 2 :(得分:1)
我会重新说出另一个来自&#34; No&#34;到&#34;不是真的&#34;。
首先澄清一点:在NodeJS中,除了代码之外,一切都是异步的。具体来说,您的代码中的一位将永远不会与您的代码的另一位并行运行 - 但NodeJS运行时可能会在您的代码执行的同时管理其他任务(即IO)。
fs.readFile
等函数的优点在于IO与代码并行发生。例如:
fs.readFile("some/file",
function(err,data){console.log("done reading file (or failed)")});
do.some("work");
当NodeJS忙于将文件读入内存时,将执行第二行代码。 fs.readFileSync
的问题在于,当你调用它时,NodeJS会停止评估你的代码(如果它都是!),直到IO完成(即文件已被读入内存,在这种情况下)。因此,如果您要问&#34;您是否可以采用阻止(可能是IO)功能并使用promises使其无阻塞?&#34;,答案绝对是&#34; no&#34;。
你能使用promises来控制阻塞函数的调用顺序吗?当然。 Promise只是一种声明调用回调的顺序的一种奇特方式 - 但你可以用诺言做的一切,你可以用setImmediate()
做(尽管清晰度要低得多)而且还有很多努力)。
答案 3 :(得分:0)
我会不同意那些说你永远不会宣传你的功能的人。在某些情况下,您希望宣传某项功能。例如,使用本机进程和类似的遗留代码库,没有使用回调和承诺,但您可以假设该函数是异步的,并将在一定时间内执行。
您希望使用promises,而不是编写大量的setTimeout()回调。
这就是我为测试目的而做的方式。检查 Ph 库,尤其是 promisify 函数,并检查它是如何用于在函数之前在中设置mocha测试。
// Initial state
var foo = 1;
var xml = "";
// Promise helper library
var Ph = (function(){
return {
delay: function (milis){
var milis = milis || 200;
return function(){
return new Promise(function(resolve, reject){
setTimeout(function(){
resolve();
}, milis)
})
}
},
promisify: function(syncFunc){
return new Promise(function(resolve, reject){
syncFunc();
resolve();
})
}
}
}());
// 'Synchronous' functions to promisify
function setXML(){
console.log("setting XML");
xml = "<bar>";
}
function setVars(){
console.log("setting Vars");
foo = 2;
}
// Test setup
before(function(done) {
this.timeout(0);
Promise.resolve()
.then(promisify(setXML))
.then(Ph.delay(3000))
.then(Ph.promisify(setVars))
.then(Ph.delay(3000))
.then(function(){
done();
})
});
// Test assertions
describe("Async setup", function(done){
it("should have XML set", function(done){
expect(xml).to.be.not.equal("");
done();
});
it("should have foo not equal 1.", function(done){
expect(foo).to.be.not.equal(1);
done();
});
it("should have foo equal to 2.", function(done){
expect(foo).to.be.equal(2);
done();
});
});
为了使它在IE中运行,我使用Promise CDN:
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-promise/4.1.1/es6-promise.auto.min.js"></script>