如何链接异步方法
how to chain async methods
我写的 API 有几个没有 return 值的异步方法,但仍应按调用顺序执行。我想从最终用户那里抽象出等待解决方案,以便他们可以链接方法调用并期望每个承诺在前一个解决后执行,如下所示:
api = new Api();
api.doAsync().doAnotherAsync().doAThirdAsync();
我们从这些方法中获取值并不重要,重要的是它们是按顺序执行的。我尝试过使用链接结构,但它并不可靠。
class Api {
resolvingMethodChain = false;
constructor() {
this._methodChain = {
next: null,
promise: Promise.resolve(),
}
}
_chain(p) {
this._methodChain.next = {
promise: p,
next: null,
};
// if we are not finished resolving the method chain, just append to the current chain
if (!this.resolvingMethodChain) this._resolveMethodChain(this._methodChain);
this._methodChain = this._methodChain.next;
return this
}
async _resolveMethodChain(chain) {
if (!this.resolvingPromiseChain) {
this.resolvingPromiseChain = true;
}
// base case
if (chain === null) {
this.resolvingPromiseChain = false;
return;
}
// resolve the promise in the current chain
await chain.promise;
// resolve the next promise in the chain
this._resolvePromiseChain(c.next);
}
}
doAsync
方法将全部遵从 _chain
,就像这样
doAsync() {
const p = new Promise(// do some async stuff);
return _chain(p); // returns this and adds the promise to the methodChain
}
我知道我可以这样写
async doAsync() {
// do async thing
return this;
}
然后像这样使用它
doAsync.then(api => api).then(...)
但是如果可以的话,我想避免从每个 then
调用中显式 returning this
对象,它看起来不像同步方式那么干净共 api.doAsync().doAnotherAsync()...
您不需要某种 API 对象来链接 async
函数。如果他们不接受您的示例代码所指示的输入值,则纯 promise-chain 方法几乎相同,没有任何样板:
async function delay (ms) {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
async function log (message) {
console.log(message)
}
async function delay1s () {
return delay(1000)
}
async function logNow () {
const seconds = Math.round(performance.now() / 1000)
return log(`${seconds}s`)
}
log('start')
.then(delay1s)
.then(logNow)
.then(delay1s)
.then(logNow)
.then(delay1s)
.then(() => log('stop'))
正如您所看到的,即使是接受输入的函数也只需要简单地包装在一个匿名箭头函数中,这非常简单,而且远非不可读。
您可以从围绕 Promise 的简单包装开始
const effect = f => x =>
(f (x), x)
const Api = (p = Promise.resolve ()) =>
({ foo: () =>
Api (p.then (effect (x => console.log ('foo', x))))
, bar: (arg) =>
Api (p.then (effect (x => console.log ('bar', arg))))
})
Api().foo().foo().bar(5)
// foo undefined
// foo undefined
// bar 5
我们可以添加其他功能来做更多有用的事情。请注意,因为我们使用的是 Promises,所以我们可以轻松地对同步或异步函数进行排序
const effect = f => x =>
(f (x), x)
const square = x =>
x * x
const Api = (p = Promise.resolve ()) =>
({ log: () =>
Api (p.then (effect (console.log)))
, foo: () =>
Api (p.then (effect (x => console.log ('foo', x))))
, bar: (arg) =>
Api (p.then (effect (x => console.log ('bar', arg))))
, then: f =>
Api (p.then (f))
})
Api().log().then(() => 5).log().then(square).log()
// undefined
// 5
// 25
现在添加任何你想要的功能。这个例子展示了实际上做一些更现实的事情的函数
const effect = f => x =>
(f (x), x)
const DB =
{ 10: { id: 10, name: 'Alice' }
, 20: { id: 20, name: 'Bob' }
}
const Database =
{ getUser: id =>
new Promise (r =>
setTimeout (r, 250, DB[id]))
}
const Api = (p = Promise.resolve ()) =>
({ log: () =>
Api (p.then (effect (console.log)))
, getUser: (id) =>
Api (p.then (() => Database.getUser (id)))
, displayName: () =>
Api (p.then (effect (user => console.log (user.name))))
})
Api().getUser(10).log().displayName().log()
// { id: 10, name: 'Alice' }
// Alice
// { id: 10, name: 'Alice' }
Api().getUser(10).log().getUser(20).log().displayName()
// { id: 10, name: 'Alice' }
// { id: 20, name: 'Bob' }
// Bob
我刚刚写了一个lib来解决这个问题https://github.com/fsvieira/asyncake
我试着让它变得非常简单,在你的情况下你只需这样做:
const asyncChain = require('asyncake');
api = new Api();
const value = await asyncChain(api).doAsync().doAnotherAsync().doAThirdAsync();
您甚至可以像这样将它添加到您的 api
class Api {
get chain () {
return asyncChain(this);
}
...
}
现在就这样使用吧:
api = new Api();
const value = await api.chain.doAsync().doAnotherAsync().doAThirdAsync();
希望对你有帮助
我写的 API 有几个没有 return 值的异步方法,但仍应按调用顺序执行。我想从最终用户那里抽象出等待解决方案,以便他们可以链接方法调用并期望每个承诺在前一个解决后执行,如下所示:
api = new Api();
api.doAsync().doAnotherAsync().doAThirdAsync();
我们从这些方法中获取值并不重要,重要的是它们是按顺序执行的。我尝试过使用链接结构,但它并不可靠。
class Api {
resolvingMethodChain = false;
constructor() {
this._methodChain = {
next: null,
promise: Promise.resolve(),
}
}
_chain(p) {
this._methodChain.next = {
promise: p,
next: null,
};
// if we are not finished resolving the method chain, just append to the current chain
if (!this.resolvingMethodChain) this._resolveMethodChain(this._methodChain);
this._methodChain = this._methodChain.next;
return this
}
async _resolveMethodChain(chain) {
if (!this.resolvingPromiseChain) {
this.resolvingPromiseChain = true;
}
// base case
if (chain === null) {
this.resolvingPromiseChain = false;
return;
}
// resolve the promise in the current chain
await chain.promise;
// resolve the next promise in the chain
this._resolvePromiseChain(c.next);
}
}
doAsync
方法将全部遵从 _chain
,就像这样
doAsync() {
const p = new Promise(// do some async stuff);
return _chain(p); // returns this and adds the promise to the methodChain
}
我知道我可以这样写
async doAsync() {
// do async thing
return this;
}
然后像这样使用它
doAsync.then(api => api).then(...)
但是如果可以的话,我想避免从每个 then
调用中显式 returning this
对象,它看起来不像同步方式那么干净共 api.doAsync().doAnotherAsync()...
您不需要某种 API 对象来链接 async
函数。如果他们不接受您的示例代码所指示的输入值,则纯 promise-chain 方法几乎相同,没有任何样板:
async function delay (ms) {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
async function log (message) {
console.log(message)
}
async function delay1s () {
return delay(1000)
}
async function logNow () {
const seconds = Math.round(performance.now() / 1000)
return log(`${seconds}s`)
}
log('start')
.then(delay1s)
.then(logNow)
.then(delay1s)
.then(logNow)
.then(delay1s)
.then(() => log('stop'))
正如您所看到的,即使是接受输入的函数也只需要简单地包装在一个匿名箭头函数中,这非常简单,而且远非不可读。
您可以从围绕 Promise 的简单包装开始
const effect = f => x =>
(f (x), x)
const Api = (p = Promise.resolve ()) =>
({ foo: () =>
Api (p.then (effect (x => console.log ('foo', x))))
, bar: (arg) =>
Api (p.then (effect (x => console.log ('bar', arg))))
})
Api().foo().foo().bar(5)
// foo undefined
// foo undefined
// bar 5
我们可以添加其他功能来做更多有用的事情。请注意,因为我们使用的是 Promises,所以我们可以轻松地对同步或异步函数进行排序
const effect = f => x =>
(f (x), x)
const square = x =>
x * x
const Api = (p = Promise.resolve ()) =>
({ log: () =>
Api (p.then (effect (console.log)))
, foo: () =>
Api (p.then (effect (x => console.log ('foo', x))))
, bar: (arg) =>
Api (p.then (effect (x => console.log ('bar', arg))))
, then: f =>
Api (p.then (f))
})
Api().log().then(() => 5).log().then(square).log()
// undefined
// 5
// 25
现在添加任何你想要的功能。这个例子展示了实际上做一些更现实的事情的函数
const effect = f => x =>
(f (x), x)
const DB =
{ 10: { id: 10, name: 'Alice' }
, 20: { id: 20, name: 'Bob' }
}
const Database =
{ getUser: id =>
new Promise (r =>
setTimeout (r, 250, DB[id]))
}
const Api = (p = Promise.resolve ()) =>
({ log: () =>
Api (p.then (effect (console.log)))
, getUser: (id) =>
Api (p.then (() => Database.getUser (id)))
, displayName: () =>
Api (p.then (effect (user => console.log (user.name))))
})
Api().getUser(10).log().displayName().log()
// { id: 10, name: 'Alice' }
// Alice
// { id: 10, name: 'Alice' }
Api().getUser(10).log().getUser(20).log().displayName()
// { id: 10, name: 'Alice' }
// { id: 20, name: 'Bob' }
// Bob
我刚刚写了一个lib来解决这个问题https://github.com/fsvieira/asyncake
我试着让它变得非常简单,在你的情况下你只需这样做:
const asyncChain = require('asyncake');
api = new Api();
const value = await asyncChain(api).doAsync().doAnotherAsync().doAThirdAsync();
您甚至可以像这样将它添加到您的 api
class Api {
get chain () {
return asyncChain(this);
}
...
}
现在就这样使用吧:
api = new Api();
const value = await api.chain.doAsync().doAnotherAsync().doAThirdAsync();
希望对你有帮助