我有一些代码可以根据我通过AJAX请求检索到服务器的场景动态生成AJAX请求。
这个想法是:
我在这里做承诺:http://jsfiddle.net/3Lddzp9j/11/
但是,我尝试编辑上面的代码,以便从最初的AJAX请求中处理一系列场景。
IE:
{
"base": {
"frequency": "5000"
},
"endpoints": [
{
"method": "GET",
"type": "JSON",
"endPoint": "https://api.github.com/users/alvarengarichard",
"queryParams": {
"objectives": "objective1, objective2, objective3"
}
},
{
"method": "GET",
"type": "JSON",
"endPoint": "https://api.github.com/users/dkang",
"queryParams": {
"objectives": "objective1, objective2, objective3"
}
}
]
这似乎是直截了当的,但问题似乎出现在" waitForTimeout"功能
我无法弄清楚如何运行多个承诺链。我有一系列承诺在"延期"变量,但链只在第一个链上继续 - 尽管是在for循环中。
任何人都可以提供有关原因的见解吗?你可以在这里看到这里发生的地方:http://jsfiddle.net/3Lddzp9j/10/
答案 0 :(得分:1)
我会尝试使用KrisKowal的q
来回答你的问题,因为我对jQuery生成的承诺并不十分熟练。
首先,我不确定你是否想要串联或并行解决承诺数组,在提出的解决方案中,我并行地解决了所有这些问题:),在系列中解决它们我会使用Q的reduce
function getScenario() { ... }
function ajaxRequest(instruction) { ... }
function createPromisifiedInstruction(instruction) {
// delay with frequency, not sure why you want to do this :(
return Q.delay(instruction.frequency)
.then(function () {
return this.ajaxRequest(instruction);
});
}
function run() {
getScenario()
.then(function (data) {
var promises = [];
var instruction;
var i;
for (i = 0; i < data.endpoints.length; i += 1) {
instruction = {
method: data.endpoints[i].method,
type: data.endpoints[i].type,
endpoint: data.endpoints[i].endPoint,
frequency: data.base.frequency
};
promises.push(createPromisifiedInstruction(instruction));
}
// alternative Q.allSettled if all the promises don't need to
// be fulfilled (some of them might be rejected)
return Q.all(promises);
})
.then(function (instructionsResults) {
// instructions results is an array with the result of each
// promisified instruction
})
.then(run)
.done();
}
run();
好的,让我解释一下上面的解决方案:
getScenario
为您提供了您开始使用的初始json(实际上返回了使用json解析的promise)ajaxRequest
ajaxRequest
会返回一个承诺,其分辨率值是请求的结果,这也意味着createPromisifiedInstruction
分辨率值将是ajaxRequest
的分辨率值Q.all
返回一个单一的承诺,当它构建的所有承诺都已解决时,它实际上做的是履行自己:),如果其中一个失败并且您实际上需要解决承诺仍然使用{ {1}} Q.allSettled
是一个数组,按照声明的顺序保存每个承诺的分辨率值答案 1 :(得分:1)
return
函数循环中有waitForTimeout
个语句。这意味着该函数将在循环的第一次迭代后返回,这就是您出错的地方。
您还使用延迟反模式,并在不需要它们的地方使用promises。除非有等待的东西,否则您无需从then
处理程序返回承诺。
关键是您需要将指令的每个映射到承诺。 Array#map
非常适合这一点。请使用正确的承诺库,not jQuery promises(编辑,但如果您绝对必须使用jQuery承诺......):
var App = (function ($) {
// Gets the scenario from the API
// NOTE: this returns a promise
var getScenario = function () {
console.log('Getting scenario ...');
return $.get('http://demo3858327.mockable.io/scenario');
};
// mapToInstructions is basically unnecessary. each instruction does
// not need its own timeout if they're all the same value, and you're not
// reshaping the original values in any significant way
// This wraps the setTimeout into a promise, again
// so we can chain it
var waitForTimeout = function(data) {
var d = $.Deferred();
setTimeout(function () {
d.resolve(data.endpoints);
}, data.base.frequency);
return d.promise();
};
var callApi = function(instruction) {
return $.ajax({
type: instruction.method,
dataType: instruction.type,
url: instruction.endPoint
});
};
// Final step: call the API from the
// provided instructions
var callApis = function(instructions) {
console.log(instructions);
console.log('Calling API with given instructions ...');
return $.when.apply($, instructions.map(callApi));
};
var handleResults = function() {
var data = Array.prototype.slice(arguments);
console.log("Handling data ...");
};
// The 'run' method
var run = function() {
getScenario()
.then(waitForTimeout)
.then(callApis)
.then(handleResults)
.then(run);
};
return {
run : run
}
})($);
App.run();
答案 2 :(得分:1)
尝试在deferred.notify
和setTimeout
内使用Number(settings.frequency) * (1 + key)
作为setTimeout
期限; msg
deferred.notify
console
在deferred.progress
回调时登录.then
,超时后 var App = (function ($) {
var getScenario = function () {
console.log("Getting scenario ...");
return $.get("http://demo3858327.mockable.io/scenario2");
};
var mapToInstruction = function (data) {
var res = $.map(data.endpoints, function(settings, key) {
return {
method:settings.method,
type:settings.type,
endpoint:settings.endPoint,
frequency:data.base.frequency
}
});
console.log("Instructions recieved:", res);
return res
};
var waitForTimeout = function(instruction) {
var res = $.when.apply(instruction,
$.map(instruction, function(settings, key) {
return new $.Deferred(function(dfd) {
setTimeout(function() {
dfd.notify("Waiting for "
+ settings.frequency
+ " ms")
.resolve(settings);
}, Number(settings.frequency) * (1 + key));
}).promise()
})
)
.then(function() {
return this
}, function(err) {
console.log("error", err)
}
, function(msg) {
console.log("\r\n" + msg + "\r\nat " + $.now() + "\r\n")
});
return res
};
var callApi = function(instruction) {
console.log("Calling API with given instructions ..."
, instruction);
var res = $.when.apply(instruction,
$.map(instruction, function(request, key) {
return request.then(function(settings) {
return $.ajax({
type: settings.method,
dataType: settings.type,
url: settings.endpoint
});
})
})
)
.then(function(data) {
return $.map(arguments, function(response, key) {
return response[0]
})
})
return res
};
var handleResults = function(data) {
console.log("Handling data ..."
, JSON.stringify(data, null, 4));
return data
};
var run = function() {
getScenario()
.then(mapToInstruction)
.then(waitForTimeout)
.then(callApi)
.then(handleResults)
.then(run);
};
return {
// This will expose only the run method
// but will keep all other functions private
run : run
}
})($);
// ... And start the app
App.run();
内的第三个函数参数
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
{{1}}
jsfiddle http://jsfiddle.net/3Lddzp9j/13/
答案 3 :(得分:1)
主要问题是:
waitForTimeout
未传递所有说明waitForTimeout
,也不会编写callApi
来执行多个ajax调用。代码还存在许多其他问题。
data
中存在预期的组件。mapToInstruction
是一个不必要的步骤 - 您可以直接从data
映射到ajax选项 - 无需进行中间数据转换。waitForTimeout
可以大大简化为单个承诺,通过一次超时解决。坚持使用jQuery,你应该得到这样的结果:
var App = (function ($) {
// Gets the scenario from the API
// sugar for $.ajax with GET as method - NOTE: this returns a promise
var getScenario = function () {
console.log('Getting scenario ...');
return $.get('http://demo3858327.mockable.io/scenario2');
};
var checkData = function (data) {
if(!data.endpoints || !data.endpoints.length) {
return $.Deferred().reject('no endpoints').promise();
}
data.base = data.base || {};
data.base.frequency = data.base.frequency || 1000;//default value
};
var waitForTimeout = function(data) {
return $.Deferred(function(dfrd) {
setTimeout(function() {
dfrd.resolve(data.endpoints);
}, data.base.frequency);
}).promise();
};
var callApi = function(endpoints) {
console.log('Calling API with given instructions ...');
return $.when.apply(null, endpoints.map(ep) {
return $.ajax({
type: ep.method,
dataType: ep.type,
url: ep.endpoint
}).then(null, function(jqXHR, textStatus, errorThrown) {
return textStatus;
});
}).then(function() {
//convert arguments to an array of results
return $.map(arguments, function(arg) {
return arg[0];
});
});
};
var handleResults = function(results) {
// results is an array of data values/objects returned by the ajax calls.
console.log("Handling data ...");
...
};
// The 'run' method
var run = function() {
getScenario()
.then(checkData)
.then(waitForTimeout)
.then(callApi)
.then(handleResults)
.then(null, function(reason) {
console.error(reason);
})
.then(run);
};
return {
run : run
}
})(jQuery);
App.run();
这将在出错时停止,但可以很容易地进行调整以继续。