2017-12-04 25 views
2

내 필요가 간단합니다. sendEmail에 대한 호출을 100 밀리 초 지연하려고합니다. 전자 메일 서비스 공급자는 초당 최대 10 개의 전자 메일을 보낼 수 있습니다.약속의 배열을 반환하는 Array.map 호출에 밀리 초 지연 추가

그러나 .map은 동기식이지만 Promise을 즉시 반환합니다.

setTimeout(() => resolve(x), 100)setTimeout(() => {return new Promise....}, 100)과 같이 아무런 쓸모가없는 setTimeout을 사용해 보았습니다.

생각하십니까?

const promises = userEmailArray.map((userEmail) => { 
    return new Promise((resolve, reject) => { 
     .... 
     mailer.sendEmail(userEmail); 
     return resolve(); 
    }); 
    }); 
}); 
... 
Promise.all(promises).then(() => resolve()).catch(error => reject(error)); 
+0

메일러는 무엇입니까? 그리고 그것이 당신에게 언제 보내 졌는지 알려주는 것은 무엇입니까? 약속이나 콜백을 통해 응답 할 때까지 기다리는 기능이 필요합니다. – Norguard

+0

Express 또는 웹 응용 프로그램에서 실행됩니까? 그렇다면 요청을 초당 10 개 이하로 제한하더라도 함수가 여러 번 호출 될 수 있기 때문에 API에 실패 할 수있는 여러 요청이 서버에 생성 될 수 있습니다. – HMR

답변

4

여러 가지 방법으로이 문제에 접근 할 수 있습니다. 아마 그냥 재귀 사슬로 묶인 약속을 사용하고 나서 이전 호출의 마무리를 기반으로 타이머를보다 정확하게 사용할 수 있으며 호출을 호출하고 오류 전파를 처리 할 수 ​​있습니다.

여기서는 mailer.sendEmail()이 node.js 콜백 호출 규칙을 따른 것으로 가정 했으므로 여기에 "promisify"해야합니다. 이미 약속을 반환하면 내가 만든 sendEmail() 함수 대신 직접 사용할 수 있습니다.

어쨌든 여기에는 다양한 접근 방식이 있습니다.

리콜 같은 기능 후 딜레이 (지연 재귀)

// make promisified version - assumes it follows node.js async calling convention 
let sendEmail = util.promisify(mailer.sendEmail); 

function delay(t, data) { 
    return new Promise(resolve => { 
     setTimeout(resolve.bind(null, data), t); 
    }); 
} 

function sendAll(array) { 
    let index = 0; 
    function next() { 
     if (index < array.length) { 
      return sendEmail(array[index++]).then(function() { 
       return delay(100).then(next); 
      }); 
     }   
    } 
    return Promise.resolve().then(next); 
} 

// usage 
sendAll(userEmailArray).then(() => { 
    // all done here 
}).catch(err => { 
    // process error here 
}); 

사용하여 setInterval을 제어 할 페이스

또한 방금 새로운 요청을 시작하는 setInterval을 사용할 수있는 모든 배열이 비어있을 때까지 100ms :

// promisify 
let sendEmail = util.promisify(mailer.sendEmail); 

function sendAll(array) { 
    return new Promise((resolve, reject) => { 
     let index = 0; 
     let timer = setInterval(function() { 
      if (index < array.length) { 
       sendEmail(array[index++]).catch(() => { 
        clearInterval(timer); 
        reject();       
       }); 
      } else { 
       clearInterval(timer); 
       resolve(); 
      } 
     }, 100); 
    }) 
} 

사용는 ES6에 await를 사용할 수 있습니다에 "일시 중지"루프를

을 루프를 일시 정지 기다리고 그리고 :

// make promisified version - assumes it follows node.js async calling convention 
let sendEmail = util.promisify(mailer.sendEmail); 

function delay(t, data) { 
    return new Promise(resolve => { 
     setTimeout(resolve.bind(null, data), t); 
    }); 
} 

const promises = userEmailArray.map(async function(userEmail) { 
    await sendEmail(userEmail).then(delay.bind(null, 100)); 
}); 
Promise.all(promises).then(() => { 
    // all done here 
}).catch(err => { 
    // process error here 
}); 

액세스 순서에 약속과 함께 사용 .reduce() 배열로

당신은 약속 .reduce()에 의해 구동 체인 사용 결과의 배열을 축적하려고하지만, 그냥 할 다음, 표준 방법을 시퀀스 싶지 않은 경우 : 블루 버드 기능 사용

// make promisified version - assumes it follows node.js async calling convention 
let sendEmail = util.promisify(mailer.sendEmail); 

function delay(t, data) { 
    return new Promise(resolve => { 
     setTimeout(resolve.bind(null, data), t); 
    }); 
} 

userEmailArray.reduce(function(p, userEmail) { 
    return p.then(() => { 
     return sendEmail(userEmail).then(delay.bind(null, 100)); 
    }); 
}, Promise.resolve()).then(() => { 
    // all done here 
}).catch(err => { 
    // process error here 
}); 

const Promise = require('Bluebird'); 
// make promisified version - assumes it follows node.js async calling convention 
let sendEmail = Promise.promisify(mailer.sendEmail); 

Promise.map(userEmailArray, userEmail => { 
    return sendEmail(userEmail).delay(100); 
}, {concurrency: 1}).then(() => { 
    // all done here 
}).catch(err => { 
    // process error here 
}); 
,536 : 동시성 제어 및 지연
모두

Bluebird promise library는 여기에 도움이 내장 된 몇 가지 유용한 기능을 가지고 있습니다

{concurrency: 1} 기능을 사용하여 동시에 여러 개의 요청이 전송되는 것을 제어하고 내장 된 .delay(100) 약속 방법을 제어합니다.


그냥 반복 사이에 지연과 배열을 시퀀싱에 대한 약간의 도우미 함수를 만드는 데 유용 할 수 있습니다, 일반적으로

를 사용할 수있는 순서 도우미를 만들고 :

function delay(t, data) { 
    return new Promise(resolve => { 
     setTimeout(resolve.bind(null, data), t); 
    }); 
} 

async function runSequence(array, delayT, fn) { 
    for (item of array) { 
     await fn(item).then(data => { 
      return delay(delayT, data); 
     }); 
    } 
} 

그러면 필요할 때마다 헬퍼를 사용할 수 있습니다.

// make promisified version - assumes it follows node.js async calling convention 
let sendEmail = util.promisify(mailer.sendEmail); 

runSequence(userEmailArray, sendEmail, 100).then(() => { 
    // all done here 
}).catch(err => { 
    // process error here 
}); 
+0

@ user1322092 - 이렇게하기 위해 총 6 가지 방법을 추가했습니다. – jfriend00

+0

정말 철저한 답변을 주셔서 감사합니다 !!! 나는'setInterval'과 함께 갔다. – user1322092

0

배열의 비동기 반복 만하는 것이 더 간단합니다.

function send(i, arr, cb) { 
    if (i >= arr.length) return cb(); 

    mailer.sendEmail(arr[i]); 
    setTimeout(send, 100, i+1, arr, cb); 
} 
send(0, userEmailArray, function() { console.log("all done") }); 
+0

감사합니다.하지만 어떻게 약속을 통합합니까? 나는 응답을 보내기 전에 모든 약속 ('Promise.all')을 필요로한다. (또는 내가 거부 당하면 오류를 리턴한다.) – user1322092

+0

@ user1322092 : 약속은 필요하지 않습니다.'send'가 'if' 문을 호출하는 콜백을 받도록하십시오. 이렇게 짧고, 더 간단하고 명확한 코드. –

0

이미 '대기열'종류가 있습니다. 보낼 주소 목록이 있습니다. 지금해야 할 일은 각각을 보내기 전에 일시 중지하는 것입니다. 그러나 각 보내기 전에 동일한 시간 동안 일시 중지하고 싶지는 않습니다. 그러면 nms의 일시 중지가 발생하고 몇 밀리 초 안에 메시지가 전송됩니다. 이 작업을 실행 해보십시오 그리고 당신은 내가 무슨 뜻인지 확인할 수 있습니다 :

const userEmailArray = [ 'one', 'two', 'three' ] 
 
const promises = userEmailArray.map(userEmail => 
 
    new Promise(resolve => 
 
    setTimeout(() => { 
 
     console.log(userEmail) 
 
     resolve() 
 
    }, 1000) 
 
) 
 
) 
 
Promise.all(promises).then(() => console.log('done'))

바라건대 당신은 두 번째에 대해, 다음 메시지의 무리가 한 번에 나타나는의 일시 중지를 보았다! 우리가 겪은 일이 아니야.

이상적으로는 차단하지 않도록 백그라운드에서 작업자 프로세스에 위임해야합니다. 그러나 지금 당장 그렇게하지 않는다고 가정 할 때, 한 가지 트릭은 각 호출이 다른 시간만큼 지연되도록하는 것입니다. (이것은 이 아니며은 큰 목록을 동시에 처리하려는 여러 사용자의 문제를 해결하지 못합니다. 아마도 동일한 API 제한이 적용될 것입니다). 여기

const userEmailArray = [ 'one', 'two', 'three' ] 
 
const promises = userEmailArray.map((userEmail, i) => 
 
    new Promise(resolve => 
 
    setTimeout(() => { 
 
     console.log(userEmail) 
 
     resolve() 
 
    }, 1000 * userEmailArray.length - 1000 * i) 
 
) 
 
) 
 
Promise.all(promises).then(() => console.log('done'))

각 배열 요소가 크게 어긋나게 처리를 참조한다. 다시 말하지만, 이것은 확장 가능한 솔루션은 아니지만 희망 사항으로는 타이밍과 약속에 대해 조금만 보여줍니다.

+1

감사합니다. Rich는 정말로 통찰력에 감사드립니다 !! – user1322092

+0

YW. 나는 규모면에서 이것을하지 않는 것에 대해 충분히 강조 할 수는 없다.) 그것은 약속이 어떻게 작동 하는지를 아직도 배우고있는 사람들에게 도움이 될 수있다. –