.then() 체인으로 이전 약속 결과에 액세스하려면 어떻게 해야 합니까?
약속에 대한 코드를 재구성하고 여러 개의 약속 체인으로 구성된 길고 평평한 약속 체인을 구축했습니다..then()
콜백마지막으로 복합값을 반환하고 여러 중간 약속 결과에 액세스해야 합니다.단, 시퀀스 중간부터의 분해능 값은 마지막 콜백의 범위에 포함되지 않습니다.이 값에 액세스하려면 어떻게 해야 합니까?
function getExample() {
return promiseA(…).then(function(resultA) {
// Some processing
return promiseB(…);
}).then(function(resultB) {
// More processing
return // How do I gain access to resultA here?
});
}
쇠사슬을 끊다
체인의 중간값에 액세스해야 할 경우 체인을 필요한 단일 조각으로 분할해야 합니다.1개의 콜백을 부가하고 그 파라미터를 여러 번 사용하는 것이 아니라 결과값이 필요한 곳에 여러 개의 콜백을 같은 약속에 부가합니다.약속은 미래의 가치일 뿐이라는 것을 잊지 마세요!선형 체인으로 다른 약속에서 파생되는 것 외에 라이브러리에서 제공된 약속 조합기를 사용하여 결과 값을 구축합니다.
이를 통해 제어 흐름이 매우 간단해지고 기능이 명확하게 구성되므로 모듈화가 쉬워집니다.
function getExample() {
var a = promiseA(…);
var b = a.then(function(resultA) {
// some processing
return promiseB(…);
});
return Promise.all([a, b]).then(function([resultA, resultB]) {
// more processing
return // something using both resultA and resultB
});
}
Promise.all
에서는 ES6, ES5에서는 ES5에서만 할 수.then
콜은 많은 약속 라이브러리(Q, Bluebird, when, ...)에서 제공되는 니프티 도우미 메서드로 대체됩니다..spread(function(resultA, resultB) { …
.
또한 Bluebird는 이를 대체하는 전용 기능을 갖추고 있습니다.Promise.all
+spread
보다 심플한(및 보다 효율적인) 구조와의 조합:
…
return Promise.join(a, b, function(resultA, resultB) { … });
ECMAScript의 조화
물론, 이 문제는 언어 디자이너들에게도 인식되었다.그들은 많은 일을 했고 비동기 함수 제안으로 마침내 성공했습니다.
ECMAScript 8
한 then
호출 또는 콜백 함수는 비동기 함수(호출 시 약속을 반환함)에서와 같이 단순히 약속이 직접 해결되기를 기다릴 수 있습니다.또한 조건, 루프 및 트라이캐치 클락스와 같은 임의 제어 구조를 특징으로 하지만 편의상 여기서는 필요하지 않다.
async function getExample() {
var resultA = await promiseA(…);
// some processing
var resultB = await promiseB(…);
// more processing
return // something using both resultA and resultB
}
ECMAScript 6
ES8을 기다리는 동안 이미 매우 유사한 구문을 사용했습니다.ES6에는 제너레이터 기능이 포함되어 있어 임의의 장소에서 실행을 분할할 수 있습니다.yield
키워드를 지정합니다.이러한 슬라이스는 서로 독립적으로, 심지어 비동기적으로 실행할 수 있습니다.다음 단계를 실행하기 전에 약속 해결을 기다리고 싶을 때 이 작업을 수행합니다.
전용 라이브러리(co 또는 task.js 등)가 있지만 많은 약속 라이브러리에는 약속을 생성하는 생성 함수를 제공할 때 이러한 비동기식 실행을 수행하는 도우미 함수(Q, Bluebird, when 등)가 있습니다.
var getExample = Promise.coroutine(function* () {
// ^^^^^^^^^^^^^^^^^ Bluebird syntax
var resultA = yield promiseA(…);
// some processing
var resultB = yield promiseB(…);
// more processing
return // something using both resultA and resultB
});
이것은 버전 4.0 이후 Node.js에서 동작했고, 또한 일부 브라우저(또는 그 개발 에디션)는 비교적 이른 시기에 제너레이터 구문을 지원했습니다.
ECMAScript 5
그러나 역호환성을 원하거나 필요로 하는 경우에는 트랜스필러 없이 사용할 수 없습니다.제너레이터 기능과 비동기 기능은 모두 현재 도구에서 지원됩니다. 예를 들어 제너레이터 및 비동기 함수의 Babel 설명서를 참조하십시오.
그 밖에도 비동기 프로그래밍을 용이하게 하기 위한 컴파일 투 JS 언어도 많이 있습니다.보통 다음과 같은 구문을 사용합니다.await
(Ice CoffeeScript 등) 단, Haskell과 같은 기능을 가진 다른 것도 있습니다.do
- 주석(예: LateJs, monadic, PureScript 또는 LispyScript).
동기 검사
변수에 약속 값을 할당한 후 동기 검사를 통해 값을 가져옵니다.를 사용하고 있습니다..value()
방법은 비슷하지만 많은 라이브러리가 비슷한 방법을 제공합니다.
function getExample() {
var a = promiseA(…);
return a.then(function() {
// some processing
return promiseB(…);
}).then(function(resultB) {
// a is guaranteed to be fulfilled here so we can just retrieve its
// value synchronously
var aValue = a.value();
});
}
이 값은 원하는 수만큼 사용할 수 있습니다.
function getExample() {
var a = promiseA(…);
var b = a.then(function() {
return promiseB(…)
});
var c = b.then(function() {
return promiseC(…);
});
var d = c.then(function() {
return promiseD(…);
});
return d.then(function() {
return a.value() + b.value() + c.value() + d.value();
});
}
네스트(및) 클로저지
변수의 범위를 유지하기 위해 클로저를 사용하는 것(이 경우 성공 콜백 함수 파라미터)은 자연스러운 JavaScript 솔루션입니다.약속으로 우리는 임의로 둥지를 틀고 평평하게 할 수 있다. .then()
콜백 - 내부 콜백의 범위를 제외하고 콜백은 의미적으로 동일합니다.
function getExample() {
return promiseA(…).then(function(resultA) {
// some processing
return promiseB(…).then(function(resultB) {
// more processing
return // something using both resultA and resultB;
});
});
}
물론, 이것은 움푹 패인 피라미드를 만드는 것입니다.들여쓰기가 너무 커지더라도 모듈화, 추가 이름 있는 함수 사용, 변수가 필요 없는 즉시 약속 체인을 평평하게 만드는 등 종말의 피라미드에 대응하기 위해 오래된 도구를 적용할 수 있습니다.
이론적으로, 실제로는 합리적인 개수만큼만 사용하면 (모든 폐쇄를 명시함으로써) 항상 세 가지 이상의 내포 수준을 피할 수 있습니다.
function getExample() {
// preprocessing
return promiseA(…).then(makeAhandler(…));
}
function makeAhandler(…)
return function(resultA) {
// some processing
return promiseB(…).then(makeBhandler(resultA, …));
};
}
function makeBhandler(resultA, …) {
return function(resultB) {
// more processing
return // anything that uses the variables in scope
};
}
이러한 종류의 부분 어플리케이션에는 도우미 기능을 사용할 수도 있습니다._.partial
언더스코어/로더쉬 또는 네이티브 메서드에서 더 많은 들여쓰기를 줄입니다.
function getExample() {
// preprocessing
return promiseA(…).then(handlerA);
}
function handlerA(resultA) {
// some processing
return promiseB(…).then(handlerB.bind(null, resultA));
}
function handlerB(resultA, resultB) {
// more processing
return // anything that uses resultA and resultB
}
명시적 패스스루
콜백의 네스트와 마찬가지로 이 기술은 클로저에 의존합니다.그러나 체인은 평탄한 상태를 유지하며, 최신 결과만 전달하는 것이 아니라 일부 상태 개체가 모든 단계에 대해 전달됩니다.이러한 상태 개체는 이전 작업의 결과를 누적하여 나중에 필요한 모든 값과 현재 작업의 결과를 제공합니다.
function getExample() {
return promiseA(…).then(function(resultA) {
// some processing
return promiseB(…).then(b => [resultA, b]); // function(b) { return [resultA, b] }
}).then(function([resultA, resultB]) {
// more processing
return // something using both resultA and resultB
});
}
그 화살표가 .b => [resultA, b]
입니다.resultA
두 결과의 배열을 다음 단계로 전달합니다.파라미터 파괴 구문을 사용하여 단일 변수로 다시 분할합니다.
할 수 있게 전에는 ES6라는 이름의 되었습니다..spread()
님은 많은 약속 라이브러리(Q, Bluebird, when, ...)에 의해 지지를 받았습니다.각 배열 요소에 하나씩 여러 개의 매개 변수를 가진 함수를 다음과 같이 사용해야 합니다..spread(function(resultA, resultB) { …
.
물론 여기서 필요한 폐쇄는 예를 들어 일부 도우미 기능을 통해 더욱 단순화할 수 있습니다.
function addTo(x) {
// imagine complex `arguments` fiddling or anything that helps usability
// but you get the idea with this simple one:
return res => [x, res];
}
…
return promiseB(…).then(addTo(resultA));
'하다'를 사용할 .Promise.all
어레이에 대한 약속을 작성합니다.
function getExample() {
return promiseA(…).then(function(resultA) {
// some processing
return Promise.all([resultA, promiseB(…)]); // resultA will implicitly be wrapped
// as if passed to Promise.resolve()
}).then(function([resultA, resultB]) {
// more processing
return // something using both resultA and resultB
});
}
어레이뿐만 아니라 임의로 복잡한 개체를 사용할 수도 있습니다.예를 들어, 다른 도우미 기능이 있거나 다른 도우미 기능이 있는 경우:
function augment(obj, name) {
return function (res) { var r = Object.assign({}, obj); r[name] = res; return r; };
}
function getExample() {
return promiseA(…).then(function(resultA) {
// some processing
return promiseB(…).then(augment({resultA}, "resultB"));
}).then(function(obj) {
// more processing
return // something using both obj.resultA and obj.resultB
});
}
이 패턴에 의해 플랫체인이 보증되고 명시적인 상태 오브젝트가 명확성을 향상시킬 수 있지만 긴 체인의 경우 지루해집니다.특히 산발적으로만 국가가 필요할 때는 모든 단계를 거쳐야 합니다.이 고정 인터페이스에서는 체인의 단일 콜백은 상당히 긴밀하게 결합되어 변경하기 어렵습니다.따라서 단일 단계를 고려하는 것이 어려워지고 다른 모듈에서 직접 콜백을 공급할 수 없습니다.콜백은 항상 상태를 고려한 보일러 플레이트 코드로 포장해야 합니다.위와 같은 추상적인 도우미 기능은 통증을 다소 완화시킬 수 있지만 항상 존재합니다.
가변 컨텍스트 상태
간단한(단, 부적절하고 오류가 발생하기 쉬운) 솔루션은 범위 높은 변수(체인 내의 모든 콜백이 액세스 가능)를 사용하여 결과값을 취득했을 때 그 변수에 쓰는 것입니다.
function getExample() {
var resultA;
return promiseA(…).then(function(_resultA) {
resultA = _resultA;
// some processing
return promiseB(…);
}).then(function(resultB) {
// more processing
return // something using both resultA and resultB
});
}
많은 변수 대신 결과가 동적으로 생성된 속성으로 저장되는 (처음에는 비어 있는) 개체를 사용할 수도 있습니다.
이 솔루션에는 몇 가지 단점이 있습니다.
- 변이 가능한 상태는 추악하고 글로벌 변수는 사악합니다.
- 이 패턴은 함수 경계를 넘어서는 동작하지 않습니다.함수의 선언이 공유 범위를 벗어나서는 안 되기 때문에 함수를 모듈화하기가 어렵습니다.
- 변수의 범위에서는 변수가 초기화되기 전에 변수에 액세스할 수 있습니다.이는 특히 레이스 조건이 발생할 수 있는 복잡한 약속 구조(루프, 분기, 배출)에서 발생할 수 있습니다.명시적으로 상태를 통과하는 것은 격려를 약속하는 선언적 설계로, 이를 방지할 수 있는 보다 깔끔한 코딩 스타일을 강요한다.
- 이러한 공유 변수의 범위를 올바르게 선택해야 합니다.예를 들어 상태가 인스턴스에 저장된 경우처럼 여러 병렬 호출 간의 레이스 조건을 방지하려면 실행된 함수에 대해 로컬이어야 합니다.
Bluebird 라이브러리는 전달되는 개체의 사용을 권장하며, 이러한 메서드를 사용하여 컨텍스트 개체를 약속 체인에 할당합니다.그렇지 않으면 usable 키워드를 사용하여 각 콜백함수에서 액세스 할 수 있습니다.오브젝트 속성은 변수보다 검출되지 않는 오타가 발생하기 쉽지만 패턴은 매우 영리합니다.
function getExample() {
return promiseA(…)
.bind({}) // Bluebird only!
.then(function(resultA) {
this.resultA = resultA;
// some processing
return promiseB(…);
}).then(function(resultB) {
// more processing
return // something using both this.resultA and resultB
}).bind(); // don't forget to unbind the object if you don't want the
// caller to access it
}
이 접근방식은 .bind를 지원하지 않는 약속 라이브러리에서 쉽게 시뮬레이션할 수 있습니다(다만 좀 더 자세한 방법으로 표현식으로 사용할 수 없습니다).
function getExample() {
var ctx = {};
return promiseA(…)
.then(function(resultA) {
this.resultA = resultA;
// some processing
return promiseB(…);
}.bind(ctx)).then(function(resultB) {
// more processing
return // something using both this.resultA and resultB
}.bind(ctx));
}
"변동 가능한 컨텍스트 상태"에 대한 덜 거친 견해
로컬 스코프 오브젝트를 사용하여 중간 결과를 약속 체인으로 수집하는 것이 질문에 대한 합리적인 접근법입니다.다음의 스니펫을 검토해 주세요.
function getExample(){
//locally scoped
const results = {};
return promiseA(paramsA).then(function(resultA){
results.a = resultA;
return promiseB(paramsB);
}).then(function(resultB){
results.b = resultB;
return promiseC(paramsC);
}).then(function(resultC){
//Resolve with composite of all promises
return Promise.resolve(results.a + results.b + resultC);
}).catch(function(error){
return Promise.reject(error);
});
}
- 글로벌 변수가 나쁘기 때문에 이 솔루션은 로컬 범위 변수를 사용하여 해를 입히지 않습니다.기능 내에서만 액세스할 수 있습니다.
- 변이 가능한 상태는 추하지만 추한 방식으로 상태가 변이되지는 않습니다.추악한 가변 상태는 전통적으로 함수 인수 또는 글로벌 변수의 상태를 변경하는 것을 의미하지만, 이 접근법은 약속 결과를 집계하는 유일한 목적으로 존재하는 로컬 범위 변수의 상태를 단순히 변경합니다.이 변수는 약속이 해결되면 간단히 사망합니다.
- 중간 약속은 결과 객체의 상태에 액세스하는 것을 막지는 않지만, 이로 인해 체인 내의 약속 중 하나가 부정하게 되어 결과를 방해할 수 있는 무서운 시나리오는 발생하지 않습니다.약속의 각 단계에서 값을 설정하는 책임은 이 함수에 한정되며, 전체적인 결과는 정확하거나 부정확합니다.몇 년 후 생산 과정에서 발생하는 버그는 아닙니다(의도하지 않는 한).
- getExample 함수를 호출할 때마다 결과 변수의 새 인스턴스가 생성되기 때문에 병렬 호출에서 발생하는 레이스 조건 시나리오는 도입되지 않습니다.
예는 jsfiddle에서 사용할 수 있습니다.
현재 노드 7.4는 하모니 플래그를 사용한 비동기/대기 콜을 지원합니다.
이것을 시험해 보세요.
async function getExample(){
let response = await returnPromise();
let response2 = await returnPromise2();
console.log(response, response2)
}
getExample()
다음 파일을 실행합니다.
node --harmony-async-await getExample.js
간단해!
다른 답은 '보다 낫다', '보다', '보다'를 요.babel-node
> 6 >
「」를 사용합니다.async - await
npm install -g babel@5.6.14
example.js:
async function getExample(){
let response = await returnPromise();
let response2 = await returnPromise2();
console.log(response, response2)
}
getExample()
다음 ''를 실행합니다.' ''를 클릭합니다.babel-node example.js
★★★★★★★★★★★★★★★★!
요즘 나 또한 너 같은 질문들을 만난다.마지막으로, 나는 퀴즈로 좋은 해결책을 찾았는데, 그것은 간단하고 읽기 좋다.이게 도움이 되었으면 좋겠어요.
체인 접속 방법에 따라
좋아, 코드를 살펴보자.
const firstPromise = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('first promise is completed');
resolve({data: '123'});
}, 2000);
});
};
const secondPromise = (someStuff) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('second promise is completed');
resolve({newData: `${someStuff.data} some more data`});
}, 2000);
});
};
const thirdPromise = (someStuff) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('third promise is completed');
resolve({result: someStuff});
}, 2000);
});
};
firstPromise()
.then(secondPromise)
.then(thirdPromise)
.then(data => {
console.log(data);
});
저는 글로벌 변수를 사용하는 것을 그다지 좋아하지 않기 때문에 이 패턴을 제 코드에 사용하지 않을 것입니다.하지만 만일의 경우에는 효과가 있을 것입니다.
사용자는 프로미스화된 Mongoose 모델입니다.
var globalVar = '';
User.findAsync({}).then(function(users){
globalVar = users;
}).then(function(){
console.log(globalVar);
});
또 다른 답변은 순차 실행자 nsynjs를 사용하는 것입니다.
function getExample(){
var response1 = returnPromise1().data;
// promise1 is resolved at this point, '.data' has the result from resolve(result)
var response2 = returnPromise2().data;
// promise2 is resolved at this point, '.data' has the result from resolve(result)
console.log(response, response2);
}
nynjs.run(getExample,{},function(){
console.log('all done');
})
업데이트: 작업 예 추가
function synchronousCode() {
var urls=[
"https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js",
"https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js",
"https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"
];
for(var i=0; i<urls.length; i++) {
var len=window.fetch(urls[i]).data.text().data.length;
// ^ ^
// | +- 2-nd promise result
// | assigned to 'data'
// |
// +-- 1-st promise result assigned to 'data'
//
console.log('URL #'+i+' : '+urls[i]+", length: "+len);
}
}
nsynjs.run(synchronousCode,{},function(){
console.log('all done');
})
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>
Bluebird를는 Bluebird를 사용할 수 ..bind
「 」 「 」 、 「 」 、 「 」
somethingAsync().bind({})
.spread(function (aValue, bValue) {
this.aValue = aValue;
this.bValue = bValue;
return somethingElseAsync(aValue, bValue);
})
.then(function (cValue) {
return this.aValue + this.bValue + cValue;
});
자세한 내용은 다음 링크를 참조하십시오.
http://bluebirdjs.com/docs/api/promise.bind.html
function getExample() {
var retA, retB;
return promiseA(…).then(function(resultA) {
retA = resultA;
// Some processing
return promiseB(…);
}).then(function(resultB) {
// More processing
//retA is value of promiseA
return // How do I gain access to resultA here?
});
}
간단한 방법:d
RSVP의 해시를 사용해도 될 것 같아요.
다음과 같은 경우:
const mainPromise = () => {
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('first promise is completed');
resolve({data: '123'});
}, 2000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('second promise is completed');
resolve({data: '456'});
}, 2000);
});
return new RSVP.hash({
prom1: promise1,
prom2: promise2
});
};
mainPromise()
.then(data => {
console.log(data.prom1);
console.log(data.prom2);
});
솔루션:
'bind'를 사용하여 나중에 'then' 함수에 중간 값을 명시적으로 넣을 수 있습니다.Promise의 동작 방식을 변경할 필요가 없으며, 에러가 이미 전파된 것처럼 값을 전파하는 데 코드 한두 줄만 있으면 되는 훌륭한 솔루션입니다.
다음으로 완전한 예를 제시하겠습니다.
// Get info asynchronously from a server
function pGetServerInfo()
{
// then value: "server info"
} // pGetServerInfo
// Write into a file asynchronously
function pWriteFile(path,string)
{
// no then value
} // pWriteFile
// The heart of the solution: Write formatted info into a log file asynchronously,
// using the pGetServerInfo and pWriteFile operations
function pLogInfo(localInfo)
{
var scope={localInfo:localInfo}; // Create an explicit scope object
var thenFunc=p2.bind(scope); // Create a temporary function with this scope
return (pGetServerInfo().then(thenFunc)); // Do the next 'then' in the chain
} // pLogInfo
// Scope of this 'then' function is {localInfo:localInfo}
function p2(serverInfo)
{
// Do the final 'then' in the chain: Writes "local info, server info"
return pWriteFile('log',this.localInfo+','+serverInfo);
} // p2
이 솔루션은 다음과 같이 호출할 수 있습니다.
pLogInfo("local info").then().catch(err);
(주의: 이 솔루션의 보다 복잡하고 완전한 버전은 테스트되었지만 이 예제 버전은 테스트되지 않았기 때문에 버그가 있을 수 있습니다.)
내가 약속에 대해 배운 것은 가능한 한 반환값이 약속을 참조하지 않을 때만 그것을 사용한다는 것이다.async/sync 구문이 특히 실용적입니다.현재 모든 최신 브라우저와 노드가 지원하고 있습니다.https://caniuse.com/ #syslogs=syslogc-syslogs는 단순한 동작이며 코드는 동기 코드를 읽는 것과 같습니다.콜백은 잊으십시오.
제가 굳이 언급할 필요가 있는 경우, 약속이란 독립적/관련되지 않은 장소에서 생성 및 해결이 이루어지는 경우입니다.그래서 인위적인 연관성과 아마도 "원격" 약속을 해결하기 위한 이벤트 청취자 대신, 나는 그 약속을 "Deferred"로 노출하는 것을 선호합니다. 다음 코드는 유효한 es5에 그것을 구현합니다.
/**
* Promise like object that allows to resolve it promise from outside code. Example:
*
```
class Api {
fooReady = new Deferred<Data>()
private knower() {
inOtherMoment(data=>{
this.fooReady.resolve(data)
})
}
}
```
*/
var Deferred = /** @class */ (function () {
function Deferred(callback) {
var instance = this;
this.resolve = null;
this.reject = null;
this.status = 'pending';
this.promise = new Promise(function (resolve, reject) {
instance.resolve = function () { this.status = 'resolved'; resolve.apply(this, arguments); };
instance.reject = function () { this.status = 'rejected'; reject.apply(this, arguments); };
});
if (typeof callback === 'function') {
callback.call(this, this.resolve, this.reject);
}
}
Deferred.prototype.then = function (resolve) {
return this.promise.then(resolve);
};
Deferred.prototype.catch = function (r) {
return this.promise.catch(r);
};
return Deferred;
}());
내 타이프스크립트 프로젝트:
좀 더 복잡한 경우, 나는 종종 이러한 작은 약속 유틸리티를 테스트하고 입력하지 않고 사용합니다. p-map은 여러 번 유용했습니다.대부분의 사용 사례를 다루었다고 생각합니다.
https://github.com/sindresorhus?utf8=%E2%9C%93&tab=repositories&q=promise&type=source&language=
언급URL : https://stackoverflow.com/questions/28250680/how-do-i-access-previous-promise-results-in-a-then-chain
'programing' 카테고리의 다른 글
휴지 상태에서는 hibernate_sequences 테이블을 생성할 수 없습니다. (0) | 2022.10.04 |
---|---|
일광 절약 시간을 염두에 두고 반복되는 날짜를 저장하는 방법 (0) | 2022.10.04 |
mariadb: jdbc: setTimestamp가 밀리초를 잘라냅니다. (0) | 2022.09.28 |
PHP 네임스페이스 및 "사용" (0) | 2022.09.28 |
마젠토는 왜 이렇게 느려? (0) | 2022.09.28 |