如果我有这样的代码:
testJSCallbacks();
function testJSCallbacks(){
var i = 0;
for (i = 0; i < 5; i++){
console.log("Step 1 "+i);
foo(i, myCB);
}
}
function foo(key, fnCB){
//retrieve png image blob from indexedDB for the key 'key'. Assume that the database is
//created and started properly
var getRequest = transaction.objectStore("store").get(key);
getRequest.onsuccess = function (event) {
var result = event.target.result;
if(result){
console.log("Step 2 " + key + " Found");
}else{
console.log("Step 2 " + key + " not Found"); //for the same 'key' value this happens before the above result is valid. i.e. key found
}
fnCB(result);
}
}
myCB = function (result){
console.log("Step 3 "+result);
}
实际输出(仅举例):
Step 1 0
Step 1 1
Step 1 2
Step 1 3
Step 1 4
Step 2 0 Found
.
.
Step 3 <result>
.
.
.
期望的输出:
Step 1 0
Step 2 0 Found
Step 3 <result value of key 0 goes here>
在我的代码中,我试图从IndexedDB中读取png blob,这些blob已经存储过了。但是在读取/搜索特定blob时,返回结果需要很长时间,同时即使先前的搜索尚未完成,也会搜索第二个blob。
如果你需要在循环中多次调用异步函数并且回调需要很长时间,那么有人可以建议你做什么/怎么做?我的代码是否正确并且具有逻辑意义,或者这不是javascript的完成方式?我对此非常陌生,来自嵌入式C背景。
答案 0 :(得分:1)
问题是getRequest.onsuccess函数是异步的,而for循环是同步执行的。这就是它首先完成的原因......实际上,当你执行testJsCallbacks时,在当前执行上下文结束并且控制返回到javascript事件队列之前,其他任何内容都不会执行,因为浏览器中的javascript执行上下文是单线程的。
为了做你想做的事,我建议使用promise library。然后你可以编写这样的代码(参见jsfiddle使用Q.js库):
testJSCallbacks();
function testJSCallbacks(){
var i = 0,
promise;
for (i = 0; i < 5; i++) {
//Make initial promise if one doesn't exist
if (!promise) {
promise = Q.fcall(getStep(i));
}
//Append to existing promise chain
else {
promise = promise.then(getStep(i));
}
//then function returns another promise that can be used for chaining.
//We are essentially chaining each function together here in the loop.
promise = promise.then(function (key) {
//Log the output of step here
console.log("Step 1 " + key);
return key;
})
//then function takes a callback function with one parammeter (the data).
//foo signature meets this criteria and will use the resolution of the last promise (key).
.then(foo)
//myCB will execute after foo resolves its promise, which it does in the onsuccess callback
.then(myCB);
}
}
function getStep(step) {
return function () {
return step;
}
}
function foo(key) {
//retrieve png image blob from indexedDB for the key 'key'. Assume that the database is
//created and started properly
var getRequest = transaction.objectStore("store").get(key),
//Need to return a promise
deferred = Q.defer();
getRequest.onsuccess = function (event) {
var result = event.target.result;
if(result){
console.log("Step 2 " + key + " Found");
}else{
console.log("Step 2 " + key + " not Found"); //for the same 'key' value this happens before the above result is valid. i.e. key found
}
deferred.resolve(result);
}
return deferred.promise;
}
function myCB (result){
console.log("Step 3: " + result);
}
jsfiddle使用setTimeout而不是objectStore来演示异步性质。
解释getStep函数:
getStep函数就像一个&#34;种子&#34;其功能在于它开始解决你想要做的事情的链(即步骤1,步骤2,步骤3)。它只是创建一个函数,返回传入的变量的值。这用于传递给promise.logs的函数,在promise解析链中执行步骤1,然后返回下一个promise解析的值(步骤2)。 JavaScript具有闭包的概念,为了获得步骤编号的正确值(而不是执行回调时的&#39; i&#39;),我们需要为变量创建一个闭包岛
为了演示,请考虑以下代码:
HTML:
<button type="button">0</button>
<button type="button">1</button>
<button type="button">2</button>
<button type="button">3</button>
<button type="button">4</button>
addHandlers();
function addHandlers() {
//Don't do this. Just demonstrating a feature:
var buttons = document.getElementsByTagName("button") || [],
i, len = buttons.length;
for (var i = 0; i < len; i++) {
buttons[i].onclick = function () {
//will always alert 5
alert(i);
}
}
}
由于for循环结束后变量i为5,因此这是函数中使用的值。这就是为什么你需要为i创建一个闭包(为了清晰起见再次使用getStep):
addHandlers();
function addHandlers() {
var buttons = document.getElementsByTagName("button") || [],
i, len = buttons.length;
for (var i = 0; i < len; i++) {
//getStep creates a function with a closure for i at its current value in the loop
buttons[i].onclick = getStep(i);
}
}
function getStep(i) {
return function () {
alert(i);
}
}