2012-03-05 8 views
10

Per un mio progetto, devo eseguire più chiamate a un'API (remota) che utilizza JSONP per elaborare la risposta dell'API. Tutte le chiamate utilizzano la stessa funzione di callback. Tutte le chiamate sono generate dinamicamente dal lato del cliente usando JavaScript.Passare un parametro aggiuntivo a un callback JSONP

Il problema è il seguente: Come faccio a passare parametri aggiuntivi a quella funzione di callback per dire la funzione sui parametri della richiesta che ho usato. Pertanto, ad esempio, nel seguente esempio, ho bisogno della funzione myCallback per sapere su id=123.

<script src="http://remote.host.com/api?id=123&jsonp=myCallback"></script> 

Esiste un modo per raggiungere questo obiettivo senza dover creare una funzione di callback separata per ciascuna delle mie chiamate? Una soluzione JavaScript vaniglia è preferibile.

EDIT:

Dopo i primi commenti e risposte ai seguenti punti si avvicinò:

  • Non ho alcun controllo sul server remoto. Quindi aggiungere il parametro alla risposta non è un'opzione.
  • Accendo più richieste contemporaneamente, quindi qualsiasi variabile per memorizzare i miei parametri non risolve il problema.
  • So che posso creare al volo più callback e assegnarli. Ma la domanda è, se posso in qualche modo evitarlo. Questo sarebbe il mio piano di riserva, se non emergessero altre soluzioni.
+0

È possibile che il server restituisca l'id come parte della risposta? Il server lo sa così da poterlo inserire nel codice di risposta. – jfriend00

+1

Stai facendo ciò che deve essere fatto. Il resto spetta all'host remoto per riconoscere i parametri. –

+0

Probabilmente sì .... jQuery renderebbe * * molto * facile, ma capisco se non vuoi includerlo. –

risposta

9

Le opzioni disponibili sono le seguenti:

  1. che il server ha messo l'ID nella risposta. Questo è il più pulito, ma spesso non è possibile modificare il codice del server.
  2. Se è possibile garantire che non vi sia mai più di una chiamata JSONP che coinvolge contemporaneamente l'ID volo, è sufficiente inserire il valore ID in una variabile globale e quando viene richiamato il callback, recuperare il valore id dalla variabile globale . Questo è semplice, ma fragile perché se ci sono più di una chiamata JSONP che coinvolge l'ID in corso allo stesso tempo, si passeranno l'una sull'altra e qualcosa non funzionerà.
  3. Generare un nome di funzione univoco per ciascuna chiamata JSONP e utilizzare una chiusura di funzione associata a tale nome di funzione per connettere l'id al callback.

Ecco un esempio della terza opzione.

È possibile utilizzare una chiusura per tenere traccia della variabile per voi, ma poiché è possibile avere contemporaneamente più chiamate JSON in volo, è necessario utilizzare un nome di funzione accessibile globalmente generato in modo univoco per ogni successiva. Chiamata JSONP.Può funzionare in questo modo:

Supponiamo che il vostro funzione che genera il tag per la JSONP è qualcosa di simile (si sostituisce qualsiasi cosa tu stia usando ora):

function doJSONP(url, callbackFuncName) { 
    var fullURL = url + "&" + callbackFuncName; 
    // generate the script tag here 
} 

Poi, si potrebbe avere un'altra funzione al di fuori di esso che fa questo:

// 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); 
} 

tuo codice principale chiamerebbe getDataForId() e la richiamata passata ad esso sarebbe passato il valore id come questo seguito da qualsiasi altri argomenti JSONP aveva sulla funzione:

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 
}); 
+0

Cioè, quello che avevo in mente come piano di riserva. Tuttavia, ciò significa creare (anche dinamicamente) più funzioni di callback, che volevo evitare. – Sirko

+0

@Sirko - A meno che non si possa garantire che SOLO abbia una sola richiesta JSONP di questo tipo in volo alla volta, è necessario fare qualcosa del genere per abbinare lo stato della richiesta al reso. Per definizione, JSONP è piuttosto stupido. Tutto ciò che fa è chiamare una funzione globale. E hai già annullato facendo in modo che il server risolva questo problema per te. Quindi, è necessario memorizzare l'ID in una posizione accessibile. Se si desidera abbinare in modo univoco l'ID corretto con la chiamata alla funzione corretta e possono esserci più chiamate di funzioni contemporaneamente, ciascuna chiamata di funzione deve essere globalmente unica. – jfriend00

+0

@Sirko - se hai solo una chiamata JSONP in volo alla volta (e puoi garantire che sia così), quindi basta memorizzare il valore dell'ID in una variabile globale e recuperarlo dalla richiamata. Questo è un modo fragile di farlo perché un sottile cambiamento nel codice da parte di qualcun altro in fondo alla strada potrebbe facilmente romperlo quando accidentalmente mettono due chiamate JSONP in volo nello stesso momento e capire il problema in quel momento sarà un disastro. – jfriend00

0

Poiché sembra che non possa commentare, devo scrivere una risposta. Ho seguito le istruzioni di jfriend00 per il mio caso ma non ho ricevuto la risposta effettiva dal server nel mio callback. Quello che ho finito per fare era questa:

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); 
} 

In sostanza, ho compattato goJSONP e getDataForUrl in 1 funzione che scrive il tag script (e lo rimuove in seguito), così come non utilizzare la funzione "non innesto", dal momento che sembrava rimuovere la risposta del server dall'array di args. Quindi estraggo i dati e richiamo la mia callback con gli argomenti disponibili. Un'altra differenza qui è che ri-uso i nomi di callback, potrei cambiarlo in nomi completamente univoci con un contatore.

Ciò che manca è la gestione del timeout. Probabilmente avvierò un timer e verificherò l'esistenza della funzione di callback. Se esiste non ha rimosso se stesso quindi è un timeout e posso agire di conseguenza.

-1

Questo è un anno ormai, ma penso che jfriend00 era sulla strada giusta, anche se è più semplice di tutto questo - utilizzare una chiusura ancora, solo, quando si specifica il callback aggiungere il param:

http://url.to.some.service?callback=myFunc (' Opta ')

http://url.to.some.service?callback=myFunc (' optB ')

Quindi utilizzare una chiusura di passare attraverso:

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 
     } 
    } 
} 
0

C'è un modo più semplice. Aggiungi il parametro al tuo URL dopo "?". E accedervi nella funzione di callback come segue.

var url = "yourURL"; 
    url += "?"+"yourparameter"; 
    $.jsonp({ 
     url: url, 
     cache: true, 
     callbackParameter: "callback", 
     callback: "cb", 
     success: onreceive, 
     error: function() { 
      console.log("data error"); 
     } 
    }); 

E la parte posteriore chiamata di funzione come segue

function onreceive(response,temp,k){ 
    var data = k.url.split("?"); 
    alert(data[1]); //gives out your parameter 
} 

Nota: È possibile aggiungere il parametro in un modo migliore nell'URL se si dispone già di altri parametri nell'URL. Ho mostrato una soluzione rapida e sporca qui.