2016-06-18 24 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();