[javascript] 약속을 차례로 (즉, 순서대로) 해결 하시겠습니까?

직렬 / 순차 방식으로 파일 배열을 읽는 다음 코드를 고려하십시오. 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. 이것이 각 약속을 순차적으로 연결하는 열쇠입니다. 다음 실행 약속 functhen화재 발생시 결과가 연결되고 그 약속이 반환 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);약속이 순차적으로 해결되도록 보장 할 수 있습니다 .