약속이있는 While 루프
프라 미스가있는 while 루프와 같은 것을 수행하는 관용적 방법은 무엇입니까? 그래서:
상태가 여전히 지속되면 뭔가를 다시 수행하고 다른 작업을 수행하십시오.
dosomething.then(possilblydomoresomethings).then(finish)
더 나은 / 더 우상적인 방법이 있는지 궁금합니다.
var q = require('q');
var index = 1;
var useless = function(){
var currentIndex = index;
console.log(currentIndex)
var deferred = q.defer();
setTimeout(function(){
if(currentIndex > 10)
deferred.resolve(false);
else deferred.resolve(true);
},500);
return deferred.promise;
}
var control = function(cont){
var deferred = q.defer();
if(cont){
index = index + 1;
useless().then(control).then(function(){
deferred.resolve();
});
}
else deferred.resolve();
return deferred.promise;
}
var chain = useless().then(control).then(function(){console.log('done')});
출력 : 12 34 5678 9 10 11 완료
개체를 사용하여 값을 래핑합니다. 이렇게 done
하면 루프가 완료되었음을 알리는 속성을 가질 수 있습니다 .
// fn should return an object like
// {
// done: false,
// value: foo
// }
function loop(promise, fn) {
return promise.then(fn).then(function (wrapper) {
return !wrapper.done ? loop(Q(wrapper.value), fn) : wrapper.value;
});
}
loop(Q.resolve(1), function (i) {
console.log(i);
return {
done: i > 10,
value: i++
};
}).done(function () {
console.log('done');
});
여기에 꽤 명확하다고 생각되는 재사용 가능한 기능이 있습니다.
var Q = require("q");
// `condition` is a function that returns a boolean
// `body` is a function that returns a promise
// returns a promise for the completion of the loop
function promiseWhile(condition, body) {
var done = Q.defer();
function loop() {
// When the result of calling `condition` is no longer true, we are
// done.
if (!condition()) return done.resolve();
// Use `when`, in case `body` does not return a promise.
// When it completes loop again otherwise, if it fails, reject the
// done promise
Q.when(body(), loop, done.reject);
}
// Start running the loop in the next tick so that this function is
// completely async. It would be unexpected if `body` was called
// synchronously the first time.
Q.nextTick(loop);
// The promise
return done.promise;
}
// Usage
var index = 1;
promiseWhile(function () { return index <= 11; }, function () {
console.log(index);
index++;
return Q.delay(500); // arbitrary async
}).then(function () {
console.log("done");
}).done();
이것이 기본 패턴을 표현하는 가장 간단한 방법입니다. 약속을 호출하고 그 결과를 확인한 다음 자신을 다시 호출하거나 종료하는 함수를 정의합니다.
const doSomething = value =>
new Promise(resolve =>
setTimeout(() => resolve(value >= 5 ? 'ok': 'no'), 1000))
const loop = value =>
doSomething(value).then(result => {
console.log(value)
if (result === 'ok') {
console.log('yay')
} else {
return loop(value + 1)
}
})
loop(1).then(() => console.log('all done!'))
해결하거나 거부하는 promise를 사용하는 경우 if 절을 사용하는 대신 then
and catch
를 정의 합니다.
약속의 배열이있는 경우 loop
매번 다음 약속 을 이동하거나 팝하도록 변경 합니다.
편집 : async/await
2018 년이기 때문에 를 사용하는 버전이 있습니다 .
const loop = async value => {
let result = null
while (result != 'ok') {
console.log(value)
result = await doSomething(value)
value = value + 1
}
console.log('yay')
}
보시다시피 일반적인 while 루프를 사용하고 재귀를 사용하지 않습니다.
이것은 q가 아닌 bluebird를위한 것이지만, bluebird api doc에서 q를 구체적으로 언급하지 않았기 때문에 저자는 promise 생성 함수를 반환하는 것이 지연을 사용하는 것보다 더 관용적이라고 언급했습니다.
var Promise = require('bluebird');
var i = 0;
var counter = Promise.method(function(){
return i++;
})
function getAll(max, results){
var results = results || [];
return counter().then(function(result){
results.push(result);
return (result < max) ? getAll(max, results) : results
})
}
getAll(10).then(function(data){
console.log(data);
})
Stuart K의 답변에 대해 언급 할 수 없으므로 여기에 약간 추가하겠습니다. Stuart K의 답변을 바탕으로 놀랍도록 간단한 개념으로 요약 할 수 있습니다. Reuse an unfulfilled promise . 그가 가진 것은 본질적으로 :
- 지연된 약속의 새 인스턴스 만들기
- 루프에서 호출하려는 함수 정의
- 그 기능 내부 :
- 완료되었는지 확인하십시오. 그리고 1 번에서 생성 한 promise를 해결하고 반환하면됩니다.
- 완료되지 않은 경우 Q에게 기존 promise를 사용하고 "재귀 적"함수 인 채워지지 않은 함수를 실행하도록 지시하거나 종료되면 실패합니다. Q.when (promise, yourFunction, failFunction)
- 함수를 정의한 후 Q를 사용하여 Q.nextTick (yourFunction)을 사용하여 처음으로 함수를 트리거합니다.
- 마지막으로 새 약속을 호출자에게 반환합니다 (모든 것이 시작되도록 트리거됩니다).
Stuart의 대답은보다 일반적인 솔루션에 대한 것이지만 기본은 훌륭합니다 (작동 방식을 알게되면).
이 패턴은 이제 q-flow 를 사용하여 더 쉽게 호출됩니다 . 위의 문제에 대한 예 :
var q = require('q');
require('q-flow');
var index = 1;
q.until(function() {
return q.delay(500).then(function() {
console.log(index++);
return index > 10;
});
}).done(function() {
return console.log('done');
});
다음은 루프 Promise
의 동작을 모방하는 프로토 타입 의 확장 for
입니다. 초기화, 조건, 루프 본문 및 증분 섹션에 대한 약속 또는 즉시 값을 지원합니다. 또한 예외를 완벽하게 지원하며 메모리 누수가 없습니다. 사용 방법에 대한 예가 아래에 나와 있습니다.
var Promise = require('promise');
// Promise.loop([properties: object]): Promise()
//
// Execute a loop based on promises. Object 'properties' is an optional
// argument with the following fields:
//
// initialization: function(): Promise() | any, optional
//
// Function executed as part of the initialization of the loop. If
// it returns a promise, the loop will not begin to execute until
// it is resolved.
//
// Any exception occurring in this function will finish the loop
// with a rejected promise. Similarly, if this function returns a
// promise, and this promise is reject, the loop finishes right
// away with a rejected promise.
//
// condition: function(): Promise(result: bool) | bool, optional
//
// Condition evaluated in the beginning of each iteration of the
// loop. The function should return a boolean value, or a promise
// object that resolves with a boolean data value.
//
// Any exception occurring during the evaluation of the condition
// will finish the loop with a rejected promise. Similarly, it this
// function returns a promise, and this promise is rejected, the
// loop finishes right away with a rejected promise.
//
// If no condition function is provided, an infinite loop is
// executed.
//
// body: function(): Promise() | any, optional
//
// Function acting as the body of the loop. If it returns a
// promise, the loop will not proceed until this promise is
// resolved.
//
// Any exception occurring in this function will finish the loop
// with a rejected promise. Similarly, if this function returns a
// promise, and this promise is reject, the loop finishes right
// away with a rejected promise.
//
// increment: function(): Promise() | any, optional
//
// Function executed at the end of each iteration of the loop. If
// it returns a promise, the condition of the loop will not be
// evaluated again until this promise is resolved.
//
// Any exception occurring in this function will finish the loop
// with a rejected promise. Similarly, if this function returns a
// promise, and this promise is reject, the loop finishes right
// away with a rejected promise.
//
Promise.loop = function(properties)
{
// Default values
properties = properties || {};
properties.initialization = properties.initialization || function() { };
properties.condition = properties.condition || function() { return true; };
properties.body = properties.body || function() { };
properties.increment = properties.increment || function() { };
// Start
return new Promise(function(resolve, reject)
{
var runInitialization = function()
{
Promise.resolve().then(function()
{
return properties.initialization();
})
.then(function()
{
process.nextTick(runCondition);
})
.catch(function(error)
{
reject(error);
});
}
var runCondition = function()
{
Promise.resolve().then(function()
{
return properties.condition();
})
.then(function(result)
{
if (result)
process.nextTick(runBody);
else
resolve();
})
.catch(function(error)
{
reject(error);
});
}
var runBody = function()
{
Promise.resolve().then(function()
{
return properties.body();
})
.then(function()
{
process.nextTick(runIncrement);
})
.catch(function(error)
{
reject(error);
});
}
var runIncrement = function()
{
Promise.resolve().then(function()
{
return properties.increment();
})
.then(function()
{
process.nextTick(runCondition);
})
.catch(function(error)
{
reject(error);
});
}
// Start running initialization
process.nextTick(runInitialization);
});
}
// Promise.delay(time: double): Promise()
//
// Returns a promise that resolves after the given delay in seconds.
//
Promise.delay = function(time)
{
return new Promise(function(resolve)
{
setTimeout(resolve, time * 1000);
});
}
// Example
var i;
Promise.loop({
initialization: function()
{
i = 2;
},
condition: function()
{
return i < 6;
},
body: function()
{
// Print "i"
console.log(i);
// Exception when 5 is reached
if (i == 5)
throw Error('Value of "i" reached 5');
// Wait 1 second
return Promise.delay(1);
},
increment: function()
{
i++;
}
})
.then(function()
{
console.log('LOOP FINISHED');
})
.catch(function(error)
{
console.log('EXPECTED ERROR:', error.message);
});
var Q = require('q')
var vetor = ['a','b','c']
function imprimeValor(elements,initValue,defer){
console.log( elements[initValue++] )
defer.resolve(initValue)
return defer.promise
}
function Qloop(initValue, elements,defer){
Q.when( imprimeValor(elements, initValue, Q.defer()), function(initValue){
if(initValue===elements.length){
defer.resolve()
}else{
defer.resolve( Qloop(initValue,elements, Q.defer()) )
}
}, function(err){
defer.reject(err)
})
return defer.promise
}
Qloop(0, vetor,Q.defer())
나는 지금 이것을 사용하고있다 :
function each(arr, work) {
function loop(arr, i) {
return new Promise(function(resolve, reject) {
if (i >= arr.length) {resolve();}
else try {
Promise.resolve(work(arr[i], i)).then(function() {
resolve(loop(arr, i+1))
}).catch(reject);
} catch(e) {reject(e);}
});
}
return loop(arr, 0);
}
이 배열 수용 arr
및 함수 work
와 리턴을 Promise
. 제공된 함수는 배열의 각 요소에 대해 한 번씩 호출되고 현재 요소와 배열의 인덱스를 전달받습니다. 동기화 또는 비동기 일 수 있으며이 경우 Promise를 반환해야합니다.
다음과 같이 사용할 수 있습니다.
var items = ['Hello', 'cool', 'world'];
each(items, function(item, idx) {
// this could simply be sync, but can also be async
// in which case it must return a Promise
return new Promise(function(resolve){
// use setTimeout to make this async
setTimeout(function(){
console.info(item, idx);
resolve();
}, 1000);
});
})
.then(function(){
console.info('DONE');
})
.catch(function(error){
console.error('Failed', error);
})
배열의 각 항목이 차례로 처리됩니다. 모두 처리되면에 제공된 코드 .then()
가 실행되거나 오류가 발생하면에 제공된 코드 가 실행됩니다 .catch()
. 내부 work
기능을 수행 할 수 있습니다 (동기 기능의 경우) 또는 (비동기 기능의 경우) 루프를 중단합니다.throw
Error
reject
Promise
function each(arr, work) {
function loop(arr, i) {
return new Promise(function(resolve, reject) {
if (i >= arr.length) {resolve();}
else try {
Promise.resolve(work(arr[i], i)).then(function() {
resolve(loop(arr, i+1))
}).catch(reject);
} catch(e) {reject(e);}
});
}
return loop(arr, 0);
}
var items = ['Hello', 'cool', 'world'];
each(items, function(item, idx) {
// this could simply be sync, but can also be async
// in which case it must return a Promise
return new Promise(function(resolve){
// use setTimeout to make this async
setTimeout(function(){
console.info(item, idx);
resolve();
}, 1000);
});
})
.then(function(){
console.info('DONE');
})
.catch(function(error){
console.error('Failed', error);
})
ES6 Promise를 사용하여 이것을 생각해 냈습니다. 약속을 연결하고 약속을 반환합니다. 기술적으로 while 루프는 아니지만 프라 미스를 동 기적으로 반복하는 방법을 보여줍니다.
function chain_promises(list, fun) {
return list.reduce(
function (promise, element) {
return promise.then(function () {
// I only needed to kick off some side-effects. If you need to get
// a list back, you would append to it here. Or maybe use
// Array.map instead of Array.reduce.
fun(element);
});
},
// An initial promise just starts things off.
Promise.resolve(true)
);
}
// To test it...
function test_function (element) {
return new Promise(function (pass, _fail) {
console.log('Processing ' + element);
pass(true);
});
}
chain_promises([1, 2, 3, 4, 5], test_function).then(function () {
console.log('Done.');
});
나는 ES6 약속을 사용하여 내 모자를 반지에 던지는 것이 좋을 것이라고 생각했습니다.
function until_success(executor){
var before_retry = undefined;
var outer_executor = function(succeed, reject){
var rejection_handler = function(err){
if(before_retry){
try {
var pre_retry_result = before_retry(err);
if(pre_retry_result)
return succeed(pre_retry_result);
} catch (pre_retry_error){
return reject(pre_retry_error);
}
}
return new Promise(executor).then(succeed, rejection_handler);
}
return new Promise(executor).then(succeed, rejection_handler);
}
var outer_promise = new Promise(outer_executor);
outer_promise.before_retry = function(func){
before_retry = func;
return outer_promise;
}
return outer_promise;
}
executor
인수는 생성자에 전달 된 인수 와 동일Promise
하지만 성공 콜백을 트리거 할 때까지 반복적으로 호출됩니다. 이 before_retry
함수는 실패한 시도에 대한 사용자 지정 오류 처리를 허용합니다. 진실한 값을 반환하면 성공의 한 형태로 간주되고 "루프"가 끝나고 그 결과가 진실됩니다. 어떤 경우 before_retry
기능이 등록되지 않거나은 falsey 값을 반환하고, 다음 루프는 다른 반복에 대해 실행됩니다. 세 번째 옵션은 before_retry
함수가 자체적으로 오류를 발생시키는 것입니다. 이 경우 "루프"가 종료되고 해당 오류를 오류로 전달합니다.
다음은 그 예입니다.
var counter = 0;
function task(succ, reject){
setTimeout(function(){
if(++counter < 5)
reject(counter + " is too small!!");
else
succ(counter + " is just right");
}, 500); // simulated async task
}
until_success(task)
.before_retry(function(err){
console.log("failed attempt: " + err);
// Option 0: return falsey value and move on to next attempt
// return
// Option 1: uncomment to get early success..
//if(err === "3 is too small!!")
// return "3 is sort of ok";
// Option 2: uncomment to get complete failure..
//if(err === "3 is too small!!")
// throw "3rd time, very unlucky";
}).then(function(val){
console.log("finally, success: " + val);
}).catch(function(err){
console.log("it didn't end well: " + err);
})
옵션 0의 출력 :
failed attempt: 1 is too small!!
failed attempt: 2 is too small!!
failed attempt: 3 is too small!!
failed attempt: 4 is too small!!
finally, success: 5 is just right
옵션 1의 출력 :
failed attempt: 1 is too small!!
failed attempt: 2 is too small!!
failed attempt: 3 is too small!!
finally, success: 3 is sort of ok
옵션 2의 출력 :
failed attempt: 1 is too small!!
failed attempt: 2 is too small!!
failed attempt: 3 is too small!!
it didn't end well: 3rd time, very unlucky
여기에 많은 답변과 달성하려는 것은 그다지 실용적이지 않습니다. 그러나 이것은 작동합니다. 이것은 aws lambda 함수에서 구현되었으며 Node.js 10에서는 함수 시간 초과까지 진행됩니다. 또한 상당한 양의 메모리를 소비 할 수 있습니다.
exports.handler = async (event) => {
let res = null;
while (true) {
try{
res = await dopromise();
}catch(err){
res = err;
}
console.log(res);
}//infinite will time out
};
function dopromise(){
return new Promise((resolve, reject) => {
//do some logic
//if error reject
//reject('failed');
resolve('success');
});
}
람다에서 테스트하고 5 분 이상 잘 실행했습니다. 그러나 다른 사람들이 말했듯이 이것은 좋은 일이 아닙니다.
나는 약속으로 비동기 작업의 체인 루프를 수행하는 데 도움이되는 모듈을 작성했으며 위의 juandopazo가 제공 한 답변을 기반으로합니다.
/**
* Should loop over a task function which returns a "wrapper" object
* until wrapper.done is true. A seed value wrapper.seed is propagated to the
* next run of the loop.
*
* todo/maybe? Reject if wrapper is not an object with done and seed keys.
*
* @param {Promise|*} seed
* @param {Function} taskFn
*
* @returns {Promise.<*>}
*/
function seedLoop(seed, taskFn) {
const seedPromise = Promise.resolve(seed);
return seedPromise
.then(taskFn)
.then((wrapper) => {
if (wrapper.done) {
return wrapper.seed;
}
return seedLoop(wrapper.seed, taskFn);
});
}
// A super simple example of counting to ten, which doesn't even
// do anything asynchronous, but if it did, it should resolve to
// a promise that returns the { done, seed } wrapper object for the
// next call of the countToTen task function.
function countToTen(count) {
const done = count > 10;
const seed = done ? count : count + 1;
return {done, seed};
}
seedLoop(1, countToTen).then((result) => {
console.log(result); // 11, the first value which was over 10.
});
https://github.com/CascadeEnergy/promise-seedloop
참고 URL : https://stackoverflow.com/questions/17217736/while-loop-with-promises
'Development Tip' 카테고리의 다른 글
gcc는 i686에서 bits / predefs.h를 찾을 수 없습니다. (0) | 2020.11.04 |
---|---|
파이썬 matplotlib 그림에서 숫자가 지수 형식으로 변경되는 것을 방지하는 방법 (0) | 2020.11.04 |
프로그래밍 방식으로 리소스 디렉터리 경로를 가져 오는 방법 (0) | 2020.11.04 |
Android 레이아웃의 자리 표시 자 텍스트 (디자인 프로세스 전용)? (0) | 2020.11.04 |
php 문자열 연결, 성능 (0) | 2020.11.04 |