可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I want to chain methods from a class. I have o problems with synchronous methods, but I don't know how to do it with asynchronous methods.
For example, this class:
class Example {
constructor() {
this.val = 0
}
async () {
setTimeout(() => {
this.val += 1
return this
}, 5000)
}
sync () {
this.val += 1
return this
}
check () {
console.log('checker', this.val)
return this
}
}
This works:
new Example().sync().check()
> 1
But this doesn't work:
new Example().async().check()
> TypeError: Cannot read property 'check' of undefined
P.S. I want chaining, not Hell Callbacks.
回答1:
I expect that you want to call check()
after the timeout has expired. The problem is that forks off, and you can't immediately have something available to return.
You could pass in check()
as a callback:
class Example {
constructor() {
this.val = 0
}
async (callback) {
setTimeout(() => {
this.val += 1
callback()
}, 5000)
}
sync () {
this.val += 1
return this
}
check () {
console.log('checker', this.val)
return this
}
}
// execution
var ex = new Example();
ex.async(ex.check)
... or a promise
class Example {
constructor() {
this.val = 0
}
async (callback) {
var deferred = Q.defer()
setTimeout(() => {
this.val += 1
deferred.resolve();
}, 5000)
return deferred.promise;
}
sync () {
this.val += 1
return this
}
check () {
console.log('checker', this.val)
return this
}
}
// execution
var ex = new Example()
ex.async().then(() => ex.check())
... Or you could use ES6 generators
回答2:
If all you want is new Example().async().check()
to work, all you need to do is return this
after you call setTimeout. Ex:
async () {
setTimeout(() => {
this.val += 1
}, 5000)
return this
}
The return this
inside the timeout isn't necessary, since it'll be executing on its own. It's basically running stand alone at that point.
Really, if you want this whole thing to be running async completely, and you're able to control flow of when certain things occur, you need to be using promises to make that happen.
回答3:
If you are using async
functions and methods, you are using promises (I suppose you know them, if not please learn about them before reading on).
You should not use setTimeout
inside of an asynchronous function if you consider waiting for it. Instead, create a promise for the timeout, preferably using a helper function like this:
function timeout(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms)
});
}
Now you can write your method like you'd really want to:
class Example {
…
async async () {
await timeout(5000);
this.val += 1;
return this;
}
…
}
Of course, as an async
function it does not return the instance itself, but a promise for it. If you are going to chain, you will have to call it inside of another asynchronous function where you can await
that promise:
(async function() {
(await (new Example().async())).check();
}());
回答4:
What you are looking for will be solved most elegantly by Promises. You'll need to likely install a polyfill, like Bluebird or q.
I would change your async method to:
async() {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
this.val += 1;
resolve(this);
}, 5000);
});
}
And your calling code to:
new Example().async().then((instance)=>instance.check());
Unfortunately, until ES7 async functions are nailed down, there won't be an elegant way to do this without some form of callback.