Node.js에서 여러 콜백을 기다리는 관용적 방법
임시 파일에 의존하는 몇 가지 작업을 수행해야한다고 가정합니다. 여기서 Node에 대해 이야기하고 있으므로 이러한 작업은 분명히 비동기 적입니다. 임시 파일을 삭제할 수있는시기를 알기 위해 모든 작업이 완료 될 때까지 기다리는 관용적 방법은 무엇입니까?
다음은 내가 원하는 작업을 보여주는 코드입니다.
do_something(tmp_file_name, function(err) {});
do_something_other(tmp_file_name, function(err) {});
fs.unlink(tmp_file_name);
그러나 이렇게 작성하면 처음 두 사람이 파일을 사용할 기회를 얻기 전에 세 번째 호출을 실행할 수 있습니다. 호출을 중첩하지 않고 계속 진행하기 전에 처음 두 호출이 이미 완료되었는지 (콜백을 호출했는지) 보장 할 수있는 방법이 필요합니다 (실제로는 동기로 만들지 않음).
콜백에 이벤트 이미 터를 사용하고 카운터를 수신자로 등록하는 것에 대해 생각했습니다. 카운터는 완료된 이벤트를 수신하고 보류중인 작업 수를 계산합니다. 마지막 작업이 끝나면 파일이 삭제됩니다. 그러나 경쟁 조건의 위험이 있으며 이것이 일반적으로 이러한 일이 수행되는 방식인지 확실하지 않습니다.
Node 사람들은 이런 종류의 문제를 어떻게 해결합니까?
최신 정보:
이제 다음을 살펴 보는 것이 좋습니다.
-
Promise 개체는 지연 및 비동기 계산에 사용됩니다. Promise는 아직 완료되지 않았지만 향후에 예상되는 작업을 나타냅니다.
인기있는 Promise 라이브러리는 bluebird 입니다. A는 약속 이유를 살펴 보라고 조언 할 것 입니다.
이를 설정하려면 promise를 사용해야합니다.
fs.readFile("file.json", function (err, val) { if (err) { console.error("unable to read file"); } else { try { val = JSON.parse(val); console.log(val.success); } catch (e) { console.error("invalid json in file"); } } });이것으로 :
fs.readFileAsync("file.json").then(JSON.parse).then(function (val) { console.log(val.success); }) .catch(SyntaxError, function (e) { console.error("invalid json in file"); }) .catch(function (e) { console.error("unable to read file"); }); 생성기 : 예를 들어 co .
Promise를 사용하여 nodejs와 브라우저를위한 제너레이터 기반 제어 흐름의 장점으로, 비 차단 코드를 멋진 방식으로 작성할 수 있습니다.
var co = require('co'); co(function *(){ // yield any promise var result = yield Promise.resolve(true); }).catch(onerror); co(function *(){ // resolve multiple promises in parallel var a = Promise.resolve(1); var b = Promise.resolve(2); var c = Promise.resolve(3); var res = yield [a, b, c]; console.log(res); // => [1, 2, 3] }).catch(onerror); // errors can be try/catched co(function *(){ try { yield Promise.reject(new Error('boom')); } catch (err) { console.error(err.message); // "boom" } }).catch(onerror); function onerror(err) { // log any uncaught errors // co will not throw any errors you do not handle!!! // HANDLE ALL YOUR ERRORS!!! console.error(err.stack); }
내가 올바르게 이해한다면 아주 좋은 비동기 라이브러리를 보아야한다고 생각합니다 . 특히 시리즈를 봐야합니다 . github 페이지에서 발췌 한 사본 :
async.series([
function(callback){
// do some stuff ...
callback(null, 'one');
},
function(callback){
// do some more stuff ...
callback(null, 'two');
},
],
// optional callback
function(err, results){
// results is now equal to ['one', 'two']
});
// an example using an object instead of an array
async.series({
one: function(callback){
setTimeout(function(){
callback(null, 1);
}, 200);
},
two: function(callback){
setTimeout(function(){
callback(null, 2);
}, 100);
},
},
function(err, results) {
// results is now equals to: {one: 1, two: 2}
});
또한이 라이브러리는 브라우저에서도 실행할 수 있습니다.
가장 간단한 방법은 비동기 작업을 시작할 때 정수 카운터를 증가시킨 다음 콜백에서 카운터를 감소시키는 것입니다. 복잡성에 따라 콜백은 카운터에서 0을 확인한 다음 파일을 삭제할 수 있습니다.
조금 더 복잡한 것은 객체 목록을 유지하는 것이며, 각 객체는 상태 코드뿐만 아니라 작업을 식별하는 데 필요한 속성 (함수 호출 일 수도 있음)을 갖습니다. 콜백은 상태 코드를 완료로 설정합니다.
그런 다음 대기 (사용 process.nextTick)하고 모든 작업이 완료되었는지 확인 하는 루프가 있습니다 . 카운터에 비해이 방법의 장점은 모든 미해결 작업이 완료 될 수있는 경우 모든 작업이 실행되기 전에 카운터 기술로 인해 파일이 조기에 삭제된다는 것입니다.
// simple countdown latch
function CDL(countdown, completion) {
this.signal = function() {
if(--countdown < 1) completion();
};
}
// usage
var latch = new CDL(10, function() {
console.log("latch.signal() was called 10 times.");
});
"네이티브"솔루션은 없지만 노드 용 흐름 제어 라이브러리 는 백만 개 입니다. Step :
Step(
function(){
do_something(tmp_file_name, this.parallel());
do_something_else(tmp_file_name, this.parallel());
},
function(err) {
if (err) throw err;
fs.unlink(tmp_file_name);
}
)
또는 Michael이 제안했듯이 카운터는 더 간단한 솔루션이 될 수 있습니다. 이 세마포어 모형을보십시오 . 다음과 같이 사용합니다.
do_something1(file, queue('myqueue'));
do_something2(file, queue('myqueue'));
queue.done('myqueue', function(){
fs.unlink(file);
});
Node : 이벤트의 핵심에서 프로그래밍 패러다임의 속도와 효율성을 활용하는 또 다른 솔루션을 제공하고 싶습니다.
Promise 또는 흐름 제어를 관리하도록 설계된 모듈 async로 수행 할 수있는 모든 작업은 이벤트와 간단한 상태 머신을 사용하여 수행 할 수 있으며, 다른 옵션보다 이해하기 쉬운 방법론을 제공한다고 생각합니다.
예를 들어 여러 파일의 길이를 병렬로 합산한다고 가정합니다.
const EventEmitter = require('events').EventEmitter;
// simple event-driven state machine
const sm = new EventEmitter();
// running state
let context={
tasks: 0, // number of total tasks
active: 0, // number of active tasks
results: [] // task results
};
const next = (result) => { // must be called when each task chain completes
if(result) { // preserve result of task chain
context.results.push(result);
}
// decrement the number of running tasks
context.active -= 1;
// when all tasks complete, trigger done state
if(!context.active) {
sm.emit('done');
}
};
// operational states
// start state - initializes context
sm.on('start', (paths) => {
const len=paths.length;
console.log(`start: beginning processing of ${len} paths`);
context.tasks = len; // total number of tasks
context.active = len; // number of active tasks
sm.emit('forEachPath', paths); // go to next state
});
// start processing of each path
sm.on('forEachPath', (paths)=>{
console.log(`forEachPath: starting ${paths.length} process chains`);
paths.forEach((path) => sm.emit('readPath', path));
});
// read contents from path
sm.on('readPath', (path) => {
console.log(` readPath: ${path}`);
fs.readFile(path,(err,buf) => {
if(err) {
sm.emit('error',err);
return;
}
sm.emit('processContent', buf.toString(), path);
});
});
// compute length of path contents
sm.on('processContent', (str, path) => {
console.log(` processContent: ${path}`);
next(str.length);
});
// when processing is complete
sm.on('done', () => {
const total = context.results.reduce((sum,n) => sum + n);
console.log(`The total of ${context.tasks} files is ${total}`);
});
// error state
sm.on('error', (err) => { throw err; });
// ======================================================
// start processing - ok, let's go
// ======================================================
sm.emit('start', ['file1','file2','file3','file4']);
다음을 출력합니다.
시작 : 4 개 경로 처리 시작 forEachPath : 4 개의 프로세스 체인 시작 readPath : file1 readPath : file2 processContent : file1 readPath : file3 processContent : file2 processContent: file3 readPath: file4 processContent: file4 The total of 4 files is 4021
Note that the ordering of the process chain tasks is dependent upon system load.
You can envision the program flow as:
start -> forEachPath -+-> readPath1 -> processContent1 -+-> done
+-> readFile2 -> processContent2 -+
+-> readFile3 -> processContent3 -+
+-> readFile4 -> processContent4 -+
For reuse, it would be trivial to create a module to support the various flow-control patterns, i.e. series, parallel, batch, while, until, etc.
The simplest solution is to run the do_something* and unlink in sequence as follows:
do_something(tmp_file_name, function(err) {
do_something_other(tmp_file_name, function(err) {
fs.unlink(tmp_file_name);
});
});
Unless, for performance reasons, you want to execute do_something() and do_something_other() in parallel, I suggest to keep it simple and go this way.
Wait.for https://github.com/luciotato/waitfor
using Wait.for:
var wait=require('wait.for');
...in a fiber...
wait.for(do_something,tmp_file_name);
wait.for(do_something_other,tmp_file_name);
fs.unlink(tmp_file_name);
With pure Promises it could be a bit more messy, but if you use Deferred Promises then it's not so bad:
Install:
npm install --save @bitbar/deferred-promise
Modify your code:
const DeferredPromise = require('@bitbar/deferred-promise');
const promises = [
new DeferredPromise(),
new DeferredPromise()
];
do_something(tmp_file_name, (err) => {
if (err) {
promises[0].reject(err);
} else {
promises[0].resolve();
}
});
do_something_other(tmp_file_name, (err) => {
if (err) {
promises[1].reject(err);
} else {
promises[1].resolve();
}
});
Promise.all(promises).then( () => {
fs.unlink(tmp_file_name);
});
참고URL : https://stackoverflow.com/questions/5172244/idiomatic-way-to-wait-for-multiple-callbacks-in-node-js
'program story' 카테고리의 다른 글
| Java와 C / C ++ 간의 프로세스 간 통신을위한 가장 빠른 (낮은 지연) 방법 (0) | 2020.08.22 |
|---|---|
| String (& String), Vec (& Vec) 또는 Box (& Box)에 대한 참조를 함수 인수로 받아들이지 않는 이유는 무엇입니까? (0) | 2020.08.22 |
| 이미지를 ASCII 아트로 변환 (0) | 2020.08.22 |
| 내가 할 수 없어야하는데 왜 TypeScript 개인 멤버에 액세스 할 수 있습니까? (0) | 2020.08.22 |
| CSS Z- 인덱스 역설 꽃 (0) | 2020.08.22 |