2016-06-18 22 views
1

我有这样的功能:如何在Javascript中使用异步条件执行“for”循环?

waitForFreeAccnt.prototype.isMemberFree = function() { 
    var self = this; 

    self.api.getMemberInfo(function() { 
     var accType = self.api.connect.accountType; 
     console.log(accType); 
     if (accType === 'FREE') { 
      console.log('it is free'); 
      return true; 
     } else { 
      console.log('it is not free'); 
      return false; 
     } 
    }); 
}; 

我想等到帐户是自由长达10秒,这样的事情:

var test = function() { 
    for (var start = 1; start < 10; start++) { 
     var result = self.isMemberFree(); 
     console.log(result); 
     if (result) { 
      break; 
     } else { 
      self.api.pause(1000); 
      console.log('waiting'); 
     } 
    } 
}; 

但它不工作,因为self.api.getMemberInfo是异步呼叫。这对Javascript非常令人沮丧。任何其他语言都会这么简单。在继续循环之前,如何强制for循环等待self.isMemberFree()完成执行?

还要注意,这不是在浏览器执行,所以我不在乎挂什么。

+0

不'self.api.connect'返回某种延期对象或承诺?在移动到下一个项目之前,您应该等待对象被解决。 – Terry

+0

@nnnnnn,我希望它拨打10次电话,并在通话之间等待1秒钟。我不在乎电话会发生多久。我们假设每次打电话只需一分钟。所以它可以等到10分10秒,我会没事的。 – Prostak

+0

@Terry,我的不好,实际上getMemberInfo返回某种对象。 – Prostak

回答

1

今天我没有我的工作笔记本电脑,因为它是星期天,我在崇高编码。如果语法有点偏差,请道歉。

为了解决您的问题,我建议更改isMemberFree()以获取回调函数。这是因为isMemberFree是异步的,并且您需要一种方式在完成工作后报告结果。

然后改变测试函数使用setTimeout API等待一秒。 将isMemberFree()函数调用为嵌套函数并递归调用,这样您就可以对异步调用进行同步控制。

看看这个编码例:

waitForFreeAccnt.prototype.isMemberFree = function (done) { 
    var self = this; 

    self.api.getMemberInfo(function() { 
     var accType = self.api.connect.accountType; 
     console.log(accType); 
     if (accType === 'FREE') { 
      console.log('it is free'); 
      return done(null, true); 
     } else { 
      console.log('it is not free'); 
      return done(null, false); 
     } 
    }); 
}; 


var test = function() { 

    var testMembership = function(waitAttempt, isFree) { 
     if (isFree) { 
      return; 
     } 
     else if (waitAttempt > 10) { 
      // wait exceeded, do something. 
      return; 
     } 
     setTimeout(function() { 
      self.isMemberFree(function(err, isFree) { 
       testMembership(waitAttempt+=1, isFree); 
      }); 
     }, /*total milliseconds in 1sec=*/1000); 
    } 

    testMembership(/*WaitAttempts=*/0, /*isFree=*/false); 
}; 

什么上面的代码确实是,想必事情已经做了会员的账户,现在测试函数被调用。因此,它等待1秒钟,然后调用isMemberFree函数,这是递归地发生,直到isMemberFree()返回true或超过10秒等待。

+0

非空闲的情况下应该可能调用'done(null,false)'。 – nnnnnn

+0

确定修改 –

+0

@SamuelToh,感谢您的帮助,但它提供了'SyntaxError:Illegal break statement'。为了简化它,你能把这两个功能合为一体吗?我的意思是,一个会包含这两个功能。 – Prostak

2

在处理异步代码时,您需要使用回调函数。也就是说,如果您要按顺序执行a()b(),但a()执行了异步操作,则需要在a()之内调用b(),一旦a()得到结果。所以不是:

a(); // does something asynchronously 
b(); // tries to use a()'s result but it isn't available yet 

...而是

a(b); // pass b to a() and a() will call it when ready 

function a(callback) { 
    triggerAsyncFunction(function(result) { 
    if (result === something) 
     callback("a just finished"); 
    }); 
} 

注意a()不按名称引用b(),它只是调用什么功能是传递作为参数。

所以将其应用于你的代码,也许是这样的:

waitForFreeAccnt.prototype.isMemberFree = function (cbf) { 
    var self = this; 
    self.api.getMemberInfo(function() { 
     cbf(self.api.connect.accountType === 'FREE'); 
    }); 
}; 
waitForFreeAccnt.prototype.testMemberXTimes = function(maxAttempts, callback) { 
    var attempts = 0; 
    var self = this; 
    (function attempt() { 
    self.isMemberFree(function(free) { 
     if (free) 
     callback(true); 
     else if (++attempts < maxAttempts) 
     setTimeout(attempt, 1000); 
     else 
     callback(false); 
    }); 
)(); 
}; 
this.testMemberXTimes(10, function(isFree) { 
    // the next part of your code here, or called from here 
    // because at this point we know we've tested up to 
    // ten times and isFree tells us the result 
}); 

注意,我编写getMemberInfo()的方式,基本上是做你的是同样的事情,但不是返回布尔它调用回调函数并传递您返回的相同布尔值。 (我删除了console.log()以使代码更短。)

还要注意,您可以将上述结构用于承诺,但最终结果将会相同。

+0

谢谢你的详细解释和你的帮助!欣赏它。不过,我会把它交给Samuel Toh,因为他的回答很早,我成功实施了。 – Prostak

2

你可以返回一个承诺

waitForFreeAccnt.prototype.isMemberFree = function() { 
    return new Promise((reject, resolve)=> 
    // set a timeout if api call takes too long 
    var timeout = setTimeout(()=> reject(Error('API timeout')), 10000); 
    // make api call 
    this.api.getMemberInfo(()=> { 
     clearTimeout(timeout); 
     resolve(this.api.connect.accountType === 'FREE'); 
    }); 
); 
}; 

然后使用它像这样

whatever.isMemberFree().then(isFree=> { 
    if (isFree) 
    console.log('it is free'); 
    else 
    console.log('it is not free'); 
}) 
// handle timeout or other errors 
.catch(err=> { 
    console.log(err.message); 
}); 
2

大厦naomik's answer,如果你这样做的,你可以很容易地使用for循环使用它,使用(最有可能)即将推出的async/await功能 - 尽管它不是ES2015的一部分。

// Note "async" here! That will make "await" work. It makes the function 
// return a promise, which you'll be able to either "await" or 
// "test().then" later. 
var test = async function() { 
    for (var start = 1; start < 10; start++) { 
     // Right here we're using "await" - it makes JavaScript *wait* for 
     // the promise that comes from self.isMemberFree() to be finished. 
     // It's really handy because you can use it in loops like "for" and 
     // "while" without changing the flow of your program! 
     var result = await self.isMemberFree(); 
     console.log(result); 
     if (result) { 
      break; 
     } else { 
      self.api.pause(1000); 
      console.log('waiting'); 
     } 
    } 
}; 

现在你需要使用一个transpiler像BabelTraceur你可以真正使用之前异步/ AWAIT,虽然。它现在是Microsoft Edge 14中的only supported

并强调从test()返回的东西不是你直接从里面返回的东西。如果我这样做:

var test = async function() { return 15; }; 
var result = test(); 

我不打算让15 - 我会得到一个承诺,将解决15:

result.then(function(res) { 
    console.log(res); // 15 
}); 

// or, use an async function again: 
var main = async function() { 
    console.log(await res); // 15 
}; 
main();