2017-06-29 54 views
0

承诺链对于不覆盖节点中的流(请参阅Bergi的评论)非常有用。但这里有一个例子,在非同步微妙的情况下产生的写流错误命令写道:具有细微异步排序的承诺链

var fs = require('fs'); 

function WTest() { 
    this.writeStream = fs.createWriteStream('junkTest.txt'); 
    this.readyToWrite = Promise.resolve(true); 
}; 

WTest.prototype.writeNext = function writeNext(text, callNum) { 
    this.readyToWrite = this.readyToWrite 
    .then(status => writeToStream(text, this.writeStream, status, callNum)); 
} 

var wTest = new WTest(); 

for (var i = 0; i < 10; i++) wTest.writeNext( 
    `${i}, some line of text that might be xxxxxxxxxxxxxxxxxxxxxxxx long \n`, i); 

function writeToStream(text, writableStream, readyToWrite, callNum) { 
    return new Promise((resolve) => { 
    if (readyToWrite) resolve(writableStream.write(text)); 
    else writableStream.once('drain',() => resolve(writableStream.write(text))); 
    }); 
} 

如果你运行它是在节点7.8,它的工作原理。但在复杂的异步情况下,它会失败,例如一行是乱序写入的。上述代码有什么异步危险?你可以在失败的地方创建一个异步的例子吗?

我的故障实例包括多个异步文件读取,其中上述代码服务于记录器功能。我已经验证了writeNext调用在调用writeNext函数后立即将它们写入控制台以达​​到预期的顺序。正如在评论中提到的,我写了两个(都直接写入同一个文件),在promise写入(=)后立即直接写入“ - ”。输出文件中的行序列是1,1 =,2,3,4,5,6,7,8,2 =,7 =,3 =,4 =,5 = 6 =,9-,9 =,10-,10 =

Bergi的大图智慧:“防止重写的唯一方法是停止生成新块。所以以上只是技术上的兴趣。创建承诺链会恶化缓冲的内存成本。

+0

“* A诺言链中节点不能过度写流*有用” - 不。你应该忽略'write'的返回值并继续写。当然,它会填充流的缓冲区,直到程序内存不足,但这仍然比缓冲承诺队列中的数据更有效。 – Bergi

+0

我看到的唯一问题是,您可能会错过'drain'事件,因为您只有在有另一个块写入时才开始收听,而不是在您收到“false”后立即收听,但这会导致永远悬挂的流,而不会导致无序写入的行。请将您的实际代码发布为“*异常复杂情况*”,因为这可能是您以意外顺序调用writeNext的地方。 – Bergi

+0

@Bergi我相信我不是以意想不到的顺序编写的,因为如果我用process.stdout.write存储调用writeNext的输出,则会按正确的顺序生成输出。 – Govdata1

回答

0

在输出文件中的行序列是
1-,1 =,2-,3-,4-,5-,6-,7-,8-,2 =,J =,3 =,4 = 5 = 6 =,9 = 9,10,10 =

从你的问题的代码不会做,当只有一个单一WTest实例,并没有别的写入该文件。

上述代码有什么异步危险?你可以在失败的地方创建一个异步的例子吗?

我看到的唯一问题是,你可能会错过一个drain事件,因为你只开始听时,有另一块写的,而不是你马上得到了false之后,但是这将导致一个永远挂流,而不是到无序写入的行。

更好:

function WTest(filename) { 
    this.writeStream = fs.createWriteStream(filename); 
    this.readyToWrite = Promise.resolve(); 
} 
WTest.prototype.writeNext = function writeNext(text, callNum) { 
    this.readyToWrite = this.readyToWrite.then(() => { 
    const status = writeStream.write(text); 
    if (!status) 
     return new Promise(resolve => this.writeStream.once('drain', resolve)); 
    }); 
};