Передайте дополнительный параметр для обратного вызова JSONP
Для моего проекта мне нужно выполнить несколько вызовов (удаленных) API с помощью JSONP для обработки ответа API. Все вызовы используют одну и ту же функцию обратного вызова. Все вызовы динамически генерируются на стороне клиента с помощью JavaScript.
Проблема заключается в следующем: как передать дополнительные параметры этой функции обратного вызова, чтобы сообщить функции о параметрах запроса, которые я использовал. Так, например, в следующем примере мне нужна функция myCallback
, чтобы узнать о id=123
.
<script src="http://remote.host.com/api?id=123&jsonp=myCallback"></script>
Есть ли способ достичь этого, не создавая отдельную функцию обратного вызова для каждого из моих вызовов? Ванильное JavaScript-решение является предпочтительным.
ИЗМЕНИТЬ
После первых комментариев и ответов появились следующие пункты:
- У меня нет никакого контроля над удаленным сервером. Поэтому добавление параметра в ответ не является вариантом.
- Я запускаю несколько запросов одновременно, поэтому любая переменная для хранения моих параметров не решает проблему.
- Я знаю, что я могу создавать множественные обратные вызовы "на лету" и назначать их. Но вопрос в том, могу ли я как-то избежать этого. Это будет мой резервный план, если не появятся другие решения.
Ответы
Ответ 1
Ваши варианты следующие:
- Попросите сервер поставить идентификатор в ответ. Это самый чистый, но часто вы не можете изменить код сервера.
- Если вы можете гарантировать, что никогда не будет более одного вызова JSONP, связанного с одновременным освещением ID, тогда вы можете просто ввести значение ID в глобальную переменную и когда вызывается обратный вызов, выберите значение id из глобальной переменной, Это просто, но хрупко, потому что, если в одно и то же время задействован один JSONP-вызов, включающий идентификатор, они будут наступать друг на друга, и что-то не будет работать.
- Создайте уникальное имя функции для каждого вызова JSONP и используйте закрытие функции, связанную с этим именем функции, чтобы подключить идентификатор к обратному вызову.
Вот пример третьего варианта.
Вы можете использовать закрытие, чтобы отслеживать переменную для вас, но поскольку вы можете одновременно использовать несколько вызовов JSON в полете, вам нужно использовать динамически генерируемое глобально доступное имя функции, которое является уникальным для каждого последующего JSONP вызов. Он может работать следующим образом:
Предположим, что ваша функция, генерирующая тег для JSONP, похожа на это (вы заменяете все, что используете сейчас):
function doJSONP(url, callbackFuncName) {
var fullURL = url + "&" + callbackFuncName;
// generate the script tag here
}
Затем у вас может быть другая функция вне этого:
// global var
var jsonpCallbacks = {cntr: 0};
function getDataForId(url, id, fn) {
// create a globally unique function name
var name = "fn" + jsonpCallbacks.cntr++;
// put that function in a globally accessible place for JSONP to call
jsonpCallbacks[name] = function() {
// upon success, remove the name
delete jsonpCallbacks[name];
// now call the desired callback internally and pass it the id
var args = Array.prototype.slice.call(arguments);
args.unshift(id);
fn.apply(this, args);
}
doJSONP(url, "jsonpCallbacks." + name);
}
Ваш основной код вызовет getDataForId()
, и переданный ему обратный вызов будет передан значение id, как это следует за любыми другими аргументами, которые JSONP имел в функции:
getDataForId(123, "http://remote.host.com/api?id=123", function(id, /* other args here*/) {
// you can process the returned data here with id available as the argument
});
Ответ 2
Так как кажется, что я не могу комментировать, я должен написать ответ. Я выполнил инструкции jfriend00 для моего случая, но не получил ответ от сервера в своем обратном вызове. То, что я закончил, было следующим:
var callbacks = {};
function doJsonCallWithExtraParams(url, id, renderCallBack) {
var safeId = id.replace(/[\.\-]/g, "_");
url = url + "?callback=callbacks." + safeId;
var s = document.createElement("script");
s.setAttribute("type", "text/javascript");
s.setAttribute("src", url);
callbacks[safeId] = function() {
delete callbacks[safeId];
var data = arguments[0];
var node = document.getElementById(id);
if (data && data.status == "200" && data.value) {
renderCallBack(data, node);
}
else {
data.value = "(error)";
renderCallBack(data, node);
}
document.body.removeChild(s);
};
document.body.appendChild(s);
}
По существу, я сжимал goJSONP и getDataForUrl в 1 функцию, которая записывает тэг script (и удаляет его позже), а также не использует функцию "unshift", поскольку это, казалось, удаляет ответ сервера из массива args. Поэтому я просто извлекаю данные и вызываю свой обратный вызов с доступными аргументами. Другое отличие здесь в том, что я повторно использую имена обратного вызова, я могу изменить это на полностью уникальные имена с помощью счетчика.
В настоящее время отсутствует время обработки тайм-аута. Вероятно, я запустил бы таймер и проверил бы существование функции обратного вызова. Если он существует, он не удалил себя, поэтому тайм-аут и я могу действовать соответственно.
Ответ 3
Там проще.
Добавьте параметр к своему URL-адресу после '?'. И получить доступ к ней в функции обратного вызова следующим образом.
var url = "yourURL";
url += "?"+"yourparameter";
$.jsonp({
url: url,
cache: true,
callbackParameter: "callback",
callback: "cb",
success: onreceive,
error: function () {
console.log("data error");
}
});
И функция обратного вызова следующая
function onreceive(response,temp,k){
var data = k.url.split("?");
alert(data[1]); //gives out your parameter
}
Примечание. Вы можете добавить этот параметр в URL-адрес лучше, если у вас уже есть другие параметры в URL-адресе. Я показал здесь быстрое грязное решение.
Ответ 4
Сейчас уже год, но я думаю, что jfriend00 был на правильном пути, хотя и проще, чем все это - использовать закрытие все еще, просто при указании обратного вызова добавьте параметр:
http://url.to.some.service?callback=myFunc ('optA')
http://url.to.some.service?callback=myFunc ('optB')
Затем используйте закрытие, чтобы передать его:
function myFunc (opt) {
var myOpt = opt; // will be optA or optB
return function (data) {
if (opt == 'optA') {
// do something with the data
}
else if (opt == 'optB') {
// do something else with the data
}
}
}