[javascript] Promise에 대한 루프를 작성하는 올바른 방법.

다음 promise 호출 과 연결된 logger.log (res) 가 반복을 통해 동 기적으로 실행 되도록 루프를 올바르게 구성하는 방법은 무엇입니까? (블루 버드)

db.getUser(email).then(function(res) { logger.log(res); }); // this is a promise

나는 다음과 같은 방법을 시도했다 ( http://blog.victorquinn.com/javascript-promise-while-loop의 방법 )

var Promise = require('bluebird');

var promiseWhile = function(condition, action) {
    var resolver = Promise.defer();

    var loop = function() {
        if (!condition()) return resolver.resolve();
        return Promise.cast(action())
            .then(loop)
            .catch(resolver.reject);
    };

    process.nextTick(loop);

    return resolver.promise;
});

var count = 0;
promiseWhile(function() {
    return count < 10;
}, function() {
    return new Promise(function(resolve, reject) {
        db.getUser(email)
          .then(function(res) { 
              logger.log(res); 
              count++;
              resolve();
          });
    }); 
}).then(function() {
    console.log('all done');
}); 

작동하는 것처럼 보이지만 logger.log (res); 호출 순서를 보장하지 않는다고 생각합니다 .

어떤 제안?



답변

logger.log (res); 호출 순서를 보장하지 않는다고 생각합니다.

사실 그렇습니다. 해당 명령문은 resolve호출 전에 실행 됩니다.

어떤 제안?

많이. 가장 중요한 것은의 사용입니다 생성 – 약속 – 수동 안티 패턴 만 할 전용 –

promiseWhile(…, function() {
    return db.getUser(email)
             .then(function(res) { 
                 logger.log(res); 
                 count++;
             });
})…

둘째, 그 while기능은 많이 단순화 될 수 있습니다.

var promiseWhile = Promise.method(function(condition, action) {
    if (!condition()) return;
    return action().then(promiseWhile.bind(null, condition, action));
});

셋째, while루프 (클로저 변수 포함)를 사용하지 않고 루프를 사용합니다 for.

var promiseFor = Promise.method(function(condition, action, value) {
    if (!condition(value)) return value;
    return action(value).then(promiseFor.bind(null, condition, action));
});

promiseFor(function(count) {
    return count < 10;
}, function(count) {
    return db.getUser(email)
             .then(function(res) { 
                 logger.log(res); 
                 return ++count;
             });
}, 0).then(console.log.bind(console, 'all done'));


답변

promiseWhen()이 목적과 다른 목적을위한 일반적인 기능을 정말로 원한다면 Bergi의 단순화를 사용하여 꼭 그렇게하세요. 그러나 promise가 작동하는 방식으로 인해 이러한 방식으로 콜백을 전달하는 것은 일반적으로 불필요하며 복잡한 작은 후프를 뛰어 넘도록합니다.

내가 당신이 노력하고 있다고 말할 수있는 한 :

  • 이메일 주소 모음에 대한 일련의 사용자 세부 정보를 비동기식으로 가져옵니다 (적어도 그게 합당한 유일한 시나리오입니다).
  • .then()재귀를 통해 체인을 구축하면 됩니다.
  • 반환 된 결과를 처리 할 때 원래 순서를 유지합니다.

따라서 정의 된 문제는 실제로 Promise Anti-patterns의 “The Collection Kerfuffle”에서 논의 된 문제이며 , 두 가지 간단한 솔루션을 제공합니다.

  • 사용하는 병렬 비동기 호출 Array.prototype.map()
  • 사용 시리얼 비동기 호출 Array.prototype.reduce().

병렬 접근 방식은 (직접적으로) 피하려는 문제를 제공합니다. 즉, 응답의 순서가 불확실합니다. 직렬 접근 방식은 .then()재귀없이 필요한 체인 을 구축합니다 .

function fetchUserDetails(arr) {
    return arr.reduce(function(promise, email) {
        return promise.then(function() {
            return db.getUser(email).done(function(res) {
                logger.log(res);
            });
        });
    }, Promise.resolve());
}

다음과 같이 전화하십시오.

//Compose here, by whatever means, an array of email addresses.
var arrayOfEmailAddys = [...];

fetchUserDetails(arrayOfEmailAddys).then(function() {
    console.log('all done');
});

보시다시피 추악한 외부 var count또는 관련 condition함수 가 필요하지 않습니다 . 제한 (문제에서 10 개)은 전적으로 배열의 길이에 의해 결정됩니다 arrayOfEmailAddys.


답변

표준 Promise 객체로 수행하는 방법은 다음과 같습니다.

// Given async function sayHi
function sayHi() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('Hi');
      resolve();
    }, 3000);
  });
}

// And an array of async functions to loop through
const asyncArray = [sayHi, sayHi, sayHi];

// We create the start of a promise chain
let chain = Promise.resolve();

// And append each function in the array to the promise chain
for (const func of asyncArray) {
  chain = chain.then(func);
}

// Output:
// Hi
// Hi (After 3 seconds)
// Hi (After 3 more seconds)


답변

주어진

  • asyncFn 함수
  • 항목 배열

필수

  • Promise Chaining .then () ‘s in series (순서대로)
  • 네이티브 es6

해결책

let asyncFn = (item) => {
  return new Promise((resolve, reject) => {
    setTimeout( () => {console.log(item); resolve(true)}, 1000 )
  })
}

// asyncFn('a')
// .then(()=>{return async('b')})
// .then(()=>{return async('c')})
// .then(()=>{return async('d')})

let a = ['a','b','c','d']

a.reduce((previous, current, index, array) => {
  return previous                                    // initiates the promise chain
  .then(()=>{return asyncFn(array[index])})      //adds .then() promise for each item
}, Promise.resolve())


답변

이를 해결하는 새로운 방법이 있으며 async / await를 사용하는 것입니다.

async function myFunction() {
  while(/* my condition */) {
    const res = await db.getUser(email);
    logger.log(res);
  }
}

myFunction().then(() => {
  /* do other stuff */
})

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
https://ponyfoo.com/articles/understanding-javascript-async-await


답변

Bergi가 제안한 기능은 정말 좋습니다.

var promiseWhile = Promise.method(function(condition, action) {
      if (!condition()) return;
    return action().then(promiseWhile.bind(null, condition, action));
});

그래도 약속을 사용할 때 의미가있는 작은 추가 작업을하고 싶습니다.

var promiseWhile = Promise.method(function(condition, action, lastValue) {
  if (!condition()) return lastValue;
  return action().then(promiseWhile.bind(null, condition, action));
});

이렇게하면 while 루프를 promise 체인에 포함하고 lastValue로 해결할 수 있습니다 (또한 action ()이 실행되지 않는 경우). 예를보십시오 :

var count = 10;
util.promiseWhile(
  function condition() {
    return count > 0;
  },
  function action() {
    return new Promise(function(resolve, reject) {
      count = count - 1;
      resolve(count)
    })
  },
  count)


답변

나는 다음과 같이 만들 것이다.

var request = []
while(count<10){
   request.push(db.getUser(email).then(function(res) { return res; }));
   count++
};

Promise.all(request).then((dataAll)=>{
  for (var i = 0; i < dataAll.length; i++) {

      logger.log(dataAll[i]);
  }
});

이런 식으로 dataAll은 기록 할 모든 요소의 정렬 된 배열입니다. 그리고 모든 약속이 완료되면 로그 작업이 수행됩니다.