如何实现重复的承诺调用链?
How can I implement a repetitive promise call chain?
请注意,我不是在寻找 reduce 模式,它假设我已经需要事先知道执行次数。这里的递归也是矫枉过正。
我需要连续执行 return 一个 Promise(已解决/已拒绝)的调用,直到在该 Promise 的 return 数据中我找到某个关键字,伪代码:
let response_that_will_come_from_each_promise = null;
do {
response_that_will_come_from_each_promise = execute_my_call_that_is_a_promise();
} while(!('finished_all_calls' in response_that_will_come_from_each_promise ));
不幸的是,这不适用于承诺的异步性质。 for 循环也不行,因为 do
和 for
都是同步的。
简而言之,我怎样才能继续调用同样发生在 return promise 中的函数 N 次,直到我发现有问题?
while
可以在 async function
:
中异步
async function pollStuff() {
while(!("keyword" in await somePromise());
}
没有异步/等待递归是你的朋友:
const pollStuff = () =>
somePromise().then(result => "keyword" in result ? "done" : pollStuff())
答案:
为了可重用性和维护,您可以创建一个构造函数来为您执行此操作:
function PromiseUntil(promiseFn, conditional) {
let trace = (place) => e => console.log(`trace: ${place} :: ${e}`);
return {
[Symbol.asyncIterator]: async function*() {
let p;
while(p != conditional) { p = await promiseFn().catch(trace("PromiseUntil[Symbol.asyncIterator]")) }
yield p;
},
resolve: async function(cb = i => i, err = trace("PromiseUntil.resolve")) {
this.resolve = false;
try {
for await (let p of this) {
this.resolved = true;
return cb(p);
}
} catch (e) {
err(e);
}
}
}
}
它只是接受一个 promise 函数并重复它,直到解析的 Promise 返回的值与条件相匹配。在下面,它检查它是否等于 "awaited value"
。一旦匹配,就会返回,并使用该值调用回调函数(在示例 console.log
中)。
示例:
function PromiseUntil(promiseFn, conditional) {
let trace = (place) => e => console.log(`trace: ${place} :: ${e}`);
return {
[Symbol.asyncIterator]: async function*() {
let p;
while(p != conditional) { p = await promiseFn().catch(trace("PromiseUntil[Symbol.asyncIterator]")) }
yield p;
},
resolve: async function(cb = i => i, err = trace("PromiseUntil.resolve")) {
this.resolve = false;
try {
for await (let p of this) {
this.resolved = true;
return cb(p);
}
} catch (e) {
err(e);
}
}
}
}
let a = () => new Promise(res => (Math.random() >= 0.5) ? (console.log("no"), res("not awaited value")) : (console.log("yes"), res("awaited value")));
let waiter = PromiseUntil(a, "awaited value");
waiter.resolve(console.log);
针对条件函数调整:
考虑以上答案后,最好传入一个条件 函数 而不仅仅是要匹配的原始值。这样你就可以检查对象 属性 或任何你想要的比较,等等
此调整将参数名称从 conditional
更改为 conditionalFn
并将 while 循环调整为:
while(p != conditional)
至:
while(p == undefined || !conditionalFn(p))
示例:
在下面我们有两个构造的PromiseUntil
对象。
- 第一个被分配了一个承诺函数,该函数将在返回的
string
包含 "fin"
时解析。
- 第二个分配了一个 promise 函数,当返回的
object
(本例中的用户对象)具有 id
属性 共 1
function PromiseUntil(promiseFn, conditionalFn) {
let trace = (place) => e => console.log(`trace: ${place} :: ${e}`);
return {
[Symbol.asyncIterator]: async function*() {
let p;
while(p == undefined || !conditionalFn(p)) {
p = await promiseFn().catch(trace("PromiseUntil[Symbol.asyncIterator]"))
}
yield p;
},
resolve: async function(cb = i => i, err = trace("PromiseUntil.resolve")) {
this.resolve = false;
try {
for await (let p of this) {
this.resolved = true;
return cb(p);
}
} catch (e) {
err(e);
}
}
}
}
let a = () => new Promise(res => (Math.random() >= 0.5) ? (console.log("First Example: no"), res("not awaited value")) : (console.log("First Example: yes"), res("First Example: finished"))),
b = () => new Promise(res => (Math.random() >= 0.5) ? (console.log("Second Example: no"), res({ id: 2 })) : (console.log("Second Example: yes"), res({id: 1, name: "John Smith" })) );
let waiter = PromiseUntil(a, val => val.includes("fin"));
waiter.resolve(console.log);
let waiter_two = PromiseUntil(b, user => user.id == 1);
waiter_two.resolve(user => console.log(`Second Example: Found User: ${user.id} , ${user.name}`));
旁白:无限循环
在上面的代码中,PromiseUntil 对象将无限期地继续尝试并解析它们的 Promise 函数。如果从未满足条件,则可能导致无限递归。为避免这种情况,您可以设置尝试和解决条件的最长持续时间,或设置尝试次数。下面演示了如何这样做:
function PromiseUntil( promiseFn, conditionalFn, {maxDuration, maxAttempts} = {} ) {
let trace = ( place ) => e => console.log( `trace: ${place} :: ${e}` ),
timeout = ( reason ) => trace( "PromiseUntil Time Out" )( Error( reason ) ),
limit = max => current => ( max && max <= current );
return {
promiseName: promiseFn.name,
[ Symbol.asyncIterator ]: async function*() {
const exceeds = {
duration: limit( Date.now() + maxDuration ),
attempts: limit( maxAttempts )
};
let attempts = 0;
let p;
while ( p == undefined || !conditionalFn( p ) ) {
if ( exceeds.duration( Date.now() ) ) throw timeout( `${this.promiseName} Max Duration Met` );
if ( exceeds.attempts( attempts++ ) ) throw timeout( `${this.promiseName} Max Resolution Attempts` );
p = await promiseFn().catch( trace( "PromiseUntil[Symbol.asyncIterator]" ) );
}
yield p;
},
resolve: async function( cb = i => i, err = trace( "PromiseUntil.resolve" ) ) {
this.resolve = false;
try {
for await ( let p of this ) {
this.resolved = true;
return cb( p );
}
}
catch ( e ) {
if ( e ) err( e );
}
}
}
}
// our promise functions
let delayedPromise = () => new Promise( res =>
setTimeout( () => Math.random() >= 0.5 ? res( "not awaited value" ) : res( "delayedPromise Successful: finished" ), 500 ) ),
attemptPromise = () => new Promise( res =>
Math.random() >= 0.5 ? res( {id: 2} ) : res( {id: 1, name: "John Smith" } ) );
// our PromiseUntil Objects
let wait_for_fin = PromiseUntil( delayedPromise, val => val.includes( "fin" ), {
maxDuration: 300
} ),
wait_for_user_one = PromiseUntil( attemptPromise, user => user.id == 1, {
maxAttempts: 2
} );
// our Resolvers
wait_for_fin.resolve( console.log );
wait_for_user_one.resolve( user => console.log( `attemptPromise Successful: Found User: ${user.id} , ${user.name}` ) );
请注意,我不是在寻找 reduce 模式,它假设我已经需要事先知道执行次数。这里的递归也是矫枉过正。
我需要连续执行 return 一个 Promise(已解决/已拒绝)的调用,直到在该 Promise 的 return 数据中我找到某个关键字,伪代码:
let response_that_will_come_from_each_promise = null;
do {
response_that_will_come_from_each_promise = execute_my_call_that_is_a_promise();
} while(!('finished_all_calls' in response_that_will_come_from_each_promise ));
不幸的是,这不适用于承诺的异步性质。 for 循环也不行,因为 do
和 for
都是同步的。
简而言之,我怎样才能继续调用同样发生在 return promise 中的函数 N 次,直到我发现有问题?
while
可以在 async function
:
async function pollStuff() {
while(!("keyword" in await somePromise());
}
没有异步/等待递归是你的朋友:
const pollStuff = () =>
somePromise().then(result => "keyword" in result ? "done" : pollStuff())
答案:
为了可重用性和维护,您可以创建一个构造函数来为您执行此操作:
function PromiseUntil(promiseFn, conditional) {
let trace = (place) => e => console.log(`trace: ${place} :: ${e}`);
return {
[Symbol.asyncIterator]: async function*() {
let p;
while(p != conditional) { p = await promiseFn().catch(trace("PromiseUntil[Symbol.asyncIterator]")) }
yield p;
},
resolve: async function(cb = i => i, err = trace("PromiseUntil.resolve")) {
this.resolve = false;
try {
for await (let p of this) {
this.resolved = true;
return cb(p);
}
} catch (e) {
err(e);
}
}
}
}
它只是接受一个 promise 函数并重复它,直到解析的 Promise 返回的值与条件相匹配。在下面,它检查它是否等于 "awaited value"
。一旦匹配,就会返回,并使用该值调用回调函数(在示例 console.log
中)。
示例:
function PromiseUntil(promiseFn, conditional) {
let trace = (place) => e => console.log(`trace: ${place} :: ${e}`);
return {
[Symbol.asyncIterator]: async function*() {
let p;
while(p != conditional) { p = await promiseFn().catch(trace("PromiseUntil[Symbol.asyncIterator]")) }
yield p;
},
resolve: async function(cb = i => i, err = trace("PromiseUntil.resolve")) {
this.resolve = false;
try {
for await (let p of this) {
this.resolved = true;
return cb(p);
}
} catch (e) {
err(e);
}
}
}
}
let a = () => new Promise(res => (Math.random() >= 0.5) ? (console.log("no"), res("not awaited value")) : (console.log("yes"), res("awaited value")));
let waiter = PromiseUntil(a, "awaited value");
waiter.resolve(console.log);
针对条件函数调整:
考虑以上答案后,最好传入一个条件 函数 而不仅仅是要匹配的原始值。这样你就可以检查对象 属性 或任何你想要的比较,等等
此调整将参数名称从 conditional
更改为 conditionalFn
并将 while 循环调整为:
while(p != conditional)
至:
while(p == undefined || !conditionalFn(p))
示例:
在下面我们有两个构造的PromiseUntil
对象。
- 第一个被分配了一个承诺函数,该函数将在返回的
string
包含"fin"
时解析。 - 第二个分配了一个 promise 函数,当返回的
object
(本例中的用户对象)具有id
属性 共1
function PromiseUntil(promiseFn, conditionalFn) {
let trace = (place) => e => console.log(`trace: ${place} :: ${e}`);
return {
[Symbol.asyncIterator]: async function*() {
let p;
while(p == undefined || !conditionalFn(p)) {
p = await promiseFn().catch(trace("PromiseUntil[Symbol.asyncIterator]"))
}
yield p;
},
resolve: async function(cb = i => i, err = trace("PromiseUntil.resolve")) {
this.resolve = false;
try {
for await (let p of this) {
this.resolved = true;
return cb(p);
}
} catch (e) {
err(e);
}
}
}
}
let a = () => new Promise(res => (Math.random() >= 0.5) ? (console.log("First Example: no"), res("not awaited value")) : (console.log("First Example: yes"), res("First Example: finished"))),
b = () => new Promise(res => (Math.random() >= 0.5) ? (console.log("Second Example: no"), res({ id: 2 })) : (console.log("Second Example: yes"), res({id: 1, name: "John Smith" })) );
let waiter = PromiseUntil(a, val => val.includes("fin"));
waiter.resolve(console.log);
let waiter_two = PromiseUntil(b, user => user.id == 1);
waiter_two.resolve(user => console.log(`Second Example: Found User: ${user.id} , ${user.name}`));
旁白:无限循环
在上面的代码中,PromiseUntil 对象将无限期地继续尝试并解析它们的 Promise 函数。如果从未满足条件,则可能导致无限递归。为避免这种情况,您可以设置尝试和解决条件的最长持续时间,或设置尝试次数。下面演示了如何这样做:
function PromiseUntil( promiseFn, conditionalFn, {maxDuration, maxAttempts} = {} ) {
let trace = ( place ) => e => console.log( `trace: ${place} :: ${e}` ),
timeout = ( reason ) => trace( "PromiseUntil Time Out" )( Error( reason ) ),
limit = max => current => ( max && max <= current );
return {
promiseName: promiseFn.name,
[ Symbol.asyncIterator ]: async function*() {
const exceeds = {
duration: limit( Date.now() + maxDuration ),
attempts: limit( maxAttempts )
};
let attempts = 0;
let p;
while ( p == undefined || !conditionalFn( p ) ) {
if ( exceeds.duration( Date.now() ) ) throw timeout( `${this.promiseName} Max Duration Met` );
if ( exceeds.attempts( attempts++ ) ) throw timeout( `${this.promiseName} Max Resolution Attempts` );
p = await promiseFn().catch( trace( "PromiseUntil[Symbol.asyncIterator]" ) );
}
yield p;
},
resolve: async function( cb = i => i, err = trace( "PromiseUntil.resolve" ) ) {
this.resolve = false;
try {
for await ( let p of this ) {
this.resolved = true;
return cb( p );
}
}
catch ( e ) {
if ( e ) err( e );
}
}
}
}
// our promise functions
let delayedPromise = () => new Promise( res =>
setTimeout( () => Math.random() >= 0.5 ? res( "not awaited value" ) : res( "delayedPromise Successful: finished" ), 500 ) ),
attemptPromise = () => new Promise( res =>
Math.random() >= 0.5 ? res( {id: 2} ) : res( {id: 1, name: "John Smith" } ) );
// our PromiseUntil Objects
let wait_for_fin = PromiseUntil( delayedPromise, val => val.includes( "fin" ), {
maxDuration: 300
} ),
wait_for_user_one = PromiseUntil( attemptPromise, user => user.id == 1, {
maxAttempts: 2
} );
// our Resolvers
wait_for_fin.resolve( console.log );
wait_for_user_one.resolve( user => console.log( `attemptPromise Successful: Found User: ${user.id} , ${user.name}` ) );