직렬 / 순차 방식으로 파일 배열을 읽는 다음 코드를 고려하십시오. readFiles
모든 파일을 순서대로 읽은 후에 만 해결되는 약속을 반환합니다.
var readFile = function(file) {
... // Returns a promise.
};
var readFiles = function(files) {
return new Promise((resolve, reject) =>
var readSequential = function(index) {
if (index >= files.length) {
resolve();
} else {
readFile(files[index]).then(function() {
readSequential(index + 1);
}).catch(reject);
}
};
readSequential(0); // Start!
});
};
위의 코드는 작동하지만 순차적으로 발생하기 위해 재귀를 수행하는 것을 좋아하지 않습니다. 이 코드를 다시 작성하여 이상한 것을 사용할 필요가없는 간단한 방법이 있습니까?readSequential
기능 있습니까?
원래 나는을 사용하려고했지만 Promise.all
모든 readFile
호출이 동시에 발생했기 때문에 원하는 것이 아닙니다 .
var readFiles = function(files) {
return Promise.all(files.map(function(file) {
return readFile(file);
}));
};
답변
2017 업데이트 : 환경에서 지원하는 경우 비동기 기능을 사용합니다.
async function readFiles(files) {
for(const file of files) {
await readFile(file);
}
};
원하는 경우 비동기 생성기를 사용하여 파일이 필요할 때까지 파일을 읽는 것을 연기 할 수 있습니다 (환경에서 지원하는 경우).
async function* readFiles(files) {
for(const file of files) {
yield await readFile(file);
}
};
업데이트 : 다시 생각하면 for 루프를 대신 사용할 수 있습니다.
var readFiles = function(files) {
var p = Promise.resolve(); // Q() in q
files.forEach(file =>
p = p.then(() => readFile(file));
);
return p;
};
또는 더 축소하여 다음을 수행하십시오.
var readFiles = function(files) {
return files.reduce((p, file) => {
return p.then(() => readFile(file));
}, Promise.resolve()); // initial
};
다른 약속 라이브러리 (예 : Bluebird)에는이를위한 유틸리티 방법이 있습니다.
예를 들어, Bluebird는 다음과 같습니다.
var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs"));
var readAll = Promise.resolve(files).map(fs.readFileAsync,{concurrency: 1 });
// if the order matters, you can use Promise.each instead and omit concurrency param
readAll.then(function(allFileContents){
// do stuff to read files.
});
오늘날 async await를 사용 하지 않는 이유는 없습니다 .
답변
다음은 일련의 작업을 선호하는 방법입니다.
function runSerial() {
var that = this;
// task1 is a function that returns a promise (and immediately starts executing)
// task2 is a function that returns a promise (and immediately starts executing)
return Promise.resolve()
.then(function() {
return that.task1();
})
.then(function() {
return that.task2();
})
.then(function() {
console.log(" ---- done ----");
});
}
더 많은 작업이있는 사례는 어떻습니까? 10 살처럼?
function runSerial(tasks) {
var result = Promise.resolve();
tasks.forEach(task => {
result = result.then(() => task());
});
return result;
}
답변
이 질문은 오래되었지만 우리는 ES6 및 기능 JavaScript의 세계에 살고 있으므로 어떻게 개선 할 수 있는지 살펴 보겠습니다.
약속은 즉시 실행되므로 약속의 배열을 만들 수는 없으며 모두 동시에 시작됩니다.
대신 약속을 반환하는 함수 배열을 만들어야합니다. 그런 다음 각 기능이 순차적으로 실행되고 약속이 시작됩니다.
우리는 이것을 몇 가지 방법으로 해결할 수 있지만 가장 좋아하는 방법은 reduce
.
reduce
약속과 함께 사용하면 약간 까다로워 지므로 하나의 라이너를 아래의 작은 소화 가능한 물기로 나눕니다.
이 함수의 본질은 reduce
초기 값으로 시작 Promise.resolve([])
하거나 빈 배열을 포함하는 약속을 사용하는 것입니다.
이 약속은 다음 reduce
과 같은 방법으로 전달 됩니다 promise
. 이것이 각 약속을 순차적으로 연결하는 열쇠입니다. 다음 실행 약속 func
은 then
화재 발생시 결과가 연결되고 그 약속이 반환 reduce
되어 다음 약속 기능으로 사이클을 실행합니다 .
모든 약속이 실행되면 반환 된 약속에는 각 약속의 모든 결과 배열이 포함됩니다.
ES6 예 (한 라이너)
/*
* serial executes Promises sequentially.
* @param {funcs} An array of funcs that return promises.
* @example
* const urls = ['/url1', '/url2', '/url3']
* serial(urls.map(url => () => $.ajax(url)))
* .then(console.log.bind(console))
*/
const serial = funcs =>
funcs.reduce((promise, func) =>
promise.then(result => func().then(Array.prototype.concat.bind(result))), Promise.resolve([]))
ES6 예 (파손)
// broken down to for easier understanding
const concat = list => Array.prototype.concat.bind(list)
const promiseConcat = f => x => f().then(concat(x))
const promiseReduce = (acc, x) => acc.then(promiseConcat(x))
/*
* serial executes Promises sequentially.
* @param {funcs} An array of funcs that return promises.
* @example
* const urls = ['/url1', '/url2', '/url3']
* serial(urls.map(url => () => $.ajax(url)))
* .then(console.log.bind(console))
*/
const serial = funcs => funcs.reduce(promiseReduce, Promise.resolve([]))
용법:
// first take your work
const urls = ['/url1', '/url2', '/url3', '/url4']
// next convert each item to a function that returns a promise
const funcs = urls.map(url => () => $.ajax(url))
// execute them serially
serial(funcs)
.then(console.log.bind(console))
답변
ES6에서 간단히 수행하려면 :
function(files) {
// Create a new empty promise (don't do that with real people ;)
var sequence = Promise.resolve();
// Loop over each file, and add on a promise to the
// end of the 'sequence' promise.
files.forEach(file => {
// Chain one computation onto the sequence
sequence =
sequence
.then(() => performComputation(file))
.then(result => doSomething(result));
// Resolves for each file, one at a time.
})
// This will resolve after the entire chain is resolved
return sequence;
}
답변
표준 Node.js 약속을위한 간단한 유틸리티 :
function sequence(tasks, fn) {
return tasks.reduce((promise, task) => promise.then(() => fn(task)), Promise.resolve());
}
최신 정보
items-promise 는 NPM 패키지를 사용할 준비가되었습니다.
답변
나는 많은 순차적 작업을 실행해야 했고이 답변을 사용하여 순차적 작업을 처리하는 기능을 위조했습니다 …
function one_by_one(objects_array, iterator, callback) {
var start_promise = objects_array.reduce(function (prom, object) {
return prom.then(function () {
return iterator(object);
});
}, Promise.resolve()); // initial
if(callback){
start_promise.then(callback);
}else{
return start_promise;
}
}
이 함수는 2 개의 인수 + 1 개의 옵션을 취합니다. 첫 번째 논쟁은 우리가 작업 할 배열입니다. 두 번째 인수는 약속 자체를 반환하는 함수 인 작업 자체이며이 약속이 해결 될 때만 다음 작업이 시작됩니다. 세 번째 인수는 모든 작업이 완료되었을 때 실행되는 콜백입니다. 콜백이 전달되지 않으면 함수는 생성 된 약속을 반환하여 끝을 처리 할 수 있습니다.
사용 예는 다음과 같습니다.
var filenames = ['1.jpg','2.jpg','3.jpg'];
var resize_task = function(filename){
//return promise of async resizing with filename
};
one_by_one(filenames,resize_task );
누군가 시간을 절약하기를 바랍니다.
답변
내가 알아낼 수 있었던 가장 좋은 해결책은 bluebird
약속이었습니다. Promise.resolve(files).each(fs.readFileAsync);
약속이 순차적으로 해결되도록 보장 할 수 있습니다 .