[javascript] Promise 배열을 순차적으로 실행하려면 어떻게해야합니까?

순차적으로 실행해야하는 일련의 약속이 있습니다.

var promises = [promise1, promise2, ..., promiseN];

RSVP.all을 호출하면 병렬로 실행됩니다.

RSVP.all(promises).then(...);

그러나 어떻게 순서대로 실행할 수 있습니까?

이렇게 수동으로 쌓을 수 있습니다

RSVP.resolve()
    .then(promise1)
    .then(promise2)
    ...
    .then(promiseN)
    .then(...);

그러나 문제는 promise의 수가 다양하고 promise의 배열이 동적으로 구축된다는 것입니다.



답변

이미 배열에 있으면 이미 실행 중입니다. 약속이 있으면 이미 실행 중입니다. 이것은 약속의 문제가 아닙니다 (IE는 메서드 Task와 관련하여 C #과 같지 않습니다 .Start()). .all아무것도 실행하지 않고 약속을 반환합니다.

약속을 반환하는 함수의 배열이있는 경우 :

var tasks = [fn1, fn2, fn3...];

tasks.reduce(function(cur, next) {
    return cur.then(next);
}, RSVP.resolve()).then(function() {
    //all executed
});

또는 값 :

var idsToDelete = [1,2,3];

idsToDelete.reduce(function(cur, next) {
    return cur.then(function() {
        return http.post("/delete.php?id=" + next);
    });
}, RSVP.resolve()).then(function() {
    //all executed
});


답변

ECMAScript 2017 비동기 함수를 사용하면 다음과 같이 수행됩니다.

async function executeSequentially() {
    const tasks = [fn1, fn2, fn3]

    for (const fn of tasks) {
        await fn()
    }
}

이제 BabelJS 를 사용하여 비동기 함수를 사용할 수 있습니다.


답변

2017 년 ES7 방식.

  <script>
  var funcs = [
    _ => new Promise(resolve => setTimeout(_ => resolve("1"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("2"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("3"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("4"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("5"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("6"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("7"), 1000))
  ];
  async function runPromisesInSequence(promises) {
    for (let promise of promises) {
      console.log(await promise());
    }
  }
  </script>
  <button onClick="runPromisesInSequence(funcs)">Do the thing</button>

이것은 주어진 함수를 병렬이 아닌 순차적으로 (하나씩) 실행합니다. 매개 변수 promises는를 반환하는 함수 배열입니다 Promise.

위 코드를 사용한 Plunker 예제 : http://plnkr.co/edit/UP0rhD?p=preview


답변

내가 더 설명하려고 노력하는 대답에 대한 두 번째 시도 :

먼저, RSVP README의 몇 가지 필수 배경 :

정말 멋진 부분은 첫 번째 핸들러에서 프라 미스를 반환 할 때 나옵니다. 이렇게하면 중첩 된 콜백을 평평하게 만들 수 있으며 많은 비동기 코드가있는 프로그램에서 “오른쪽 드리프트”를 방지하는 프라 미스의 주요 기능입니다.

이것이 바로 then그 전에 끝나야 할 약속에서 나중 약속을 반환함으로써 순차적으로 약속을 만드는 방법 입니다.

이러한 일련의 약속을 트리로 생각하면 분기가 순차적 프로세스를 나타내고 잎이 동시 프로세스를 나타내는 트리라고 생각하면 도움이됩니다.

이러한 약속 트리를 구축하는 프로세스는 다른 종류의 트리를 구축하는 매우 일반적인 작업과 유사합니다. 트리에서 현재 분기를 추가하는 위치에 대한 포인터 또는 참조를 유지하고 반복적으로 항목을 추가합니다.

@Esailija가 그의 대답에서 지적했듯이 인수를 사용하지 않는 약속 반환 함수 배열이 있으면 reduce트리를 깔끔하게 구축하는 데 사용할 수 있습니다. reduce를 직접 구현 한 적이 있다면 @Esailija의 답변에서 reduce가 수행하는 작업은 현재 약속 ( cur)에 대한 참조를 유지하고 각 약속이 then.

함수를 반환하는 것을 약속하는 동종의 좋은 배열이없는 경우 또는 단순한 선형 시퀀스보다 더 복잡한 구조가 필요한 경우 다음을 유지하여 약속 트리를 직접 구성 할 수 있습니다. 새 약속을 추가하려는 약속 트리의 위치에 대한 참조 :

var root_promise = current_promise = Ember.Deferred.create();
// you can also just use your first real promise as the root; the advantage of  
// using an empty one is in the case where the process of BUILDING your tree of 
// promises is also asynchronous and you need to make sure it is built first 
// before starting it

current_promise = current_promise.then(function(){
  return // ...something that returns a promise...;
});

current_promise = current_promise.then(function(){
  return // ...something that returns a promise...;
});

// etc.

root_promise.resolve();

RSVP.all을 사용하여 약속 “분기”에 여러 “휴가”를 추가하여 동시 및 순차 프로세스의 조합을 작성할 수 있습니다. 너무 복잡하기 때문에 내 반대 투표가 그 예를 보여줍니다.

Ember.run.scheduleOnce ( ‘afterRender’)를 사용하여 다음 약속이 실행되기 전에 하나의 약속에서 수행 된 작업이 렌더링되도록 할 수 있습니다. 내가 너무 복잡하기 때문에 반대표를 던진 답변도 그 예를 보여줍니다.


답변

또 다른 접근 방식은 프로토 타입 에 전역 시퀀스 함수 를 정의하는 것 Promise입니다.

Promise.prototype.sequence = async (promiseFns) => {
  for (let promiseFn of promiseFns) {
    await promiseFn();
  }
}

그런 다음 어디서나 사용할 수 있습니다. Promise.all()

const timeout = async ms => new Promise(resolve =>
  setTimeout(() => {
    console.log("done", ms);
    resolve();
  }, ms)
);

// Executed one after the other
await Promise.sequence([() => timeout(1000), () => timeout(500)]);
// done: 1000
// done: 500

// Executed in parallel
await Promise.all([timeout(1000), timeout(500)]);
// done: 500
// done: 1000

면책 조항 : 프로토 타입을 신중하게 편집하십시오!


답변

for루프 를 해결하려면 모든 것이 필요합니다. 🙂

var promises = [a,b,c];
var chain;

for(let i in promises){
  if(chain) chain = chain.then(promises[i]);
  if(!chain) chain = promises[i]();
}

function a(){
  return new Promise((resolve)=>{
    setTimeout(function(){
      console.log('resolve A');
      resolve();
    },1000);
  });
}
function b(){
  return new Promise((resolve)=>{
    setTimeout(function(){
      console.log('resolve B');
      resolve();
    },500);
  });
}
function c(){
  return new Promise((resolve)=>{
    setTimeout(function(){
      console.log('resolve C');
      resolve();
    },100);
  });
}


답변

비슷한 문제가 있었고 순차적으로 함수를 하나씩 실행하는 재귀 함수를 만들었습니다.

var tasks = [fn1, fn2, fn3];

var executeSequentially = function(tasks) {
  if (tasks && tasks.length > 0) {
    var task = tasks.shift();

    return task().then(function() {
      return executeSequentially(tasks);
    });
  }

  return Promise.resolve();
};

다음 함수에서 출력을 수집해야하는 경우 :

var tasks = [fn1, fn2, fn3];

var executeSequentially = function(tasks) {
  if (tasks && tasks.length > 0) {
    var task = tasks.shift();

    return task().then(function(output) {
      return executeSequentially(tasks).then(function(outputs) {
        outputs.push(output);

        return Promise.resolve(outputs);
      });
    });
  }

  return Promise.resolve([]);
};