Передайте дополнительный параметр для обратного вызова 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
        }
    }
}