1. 程式人生 > >【web】jsonp原始碼學習

【web】jsonp原始碼學習

API

jsonp(url, opts, fn)

  • url (String) url to fetch
  • opts (Object), optional

    • param (String) name of the query string parameter to specify the

    • callback (defaults to callback)

    • timeout (Number) how long after a timeout error is emitted. 0 to
      disable (defaults to 60000)

    • prefix(String) prefix for the global callback functions that handle
      jsonp responses (defaults to __jp)

    • name (String) name of the global callback functions that handle
      jsonp responses (defaults to prefix +incremented counter)

  • fn callback

The callback is called with err, data parameters.

If it times out, the err will be an Error object whose message is Timeout.

Returns a function that, when called, will cancel the in-progress jsonp request (fn won’t be called).

使用:

var jsonp = require('../');
var querystring = require('querystring');
var test = require('tape');

// See http://doc.jsfiddle.net/use/echo.html
var ENDPOINT = 'http://jsfiddle.net/echo/jsonp/';

test('basic jsonp', function (t) {
  t.plan(1);
  var obj = {
    beep: 'boop',
    yo: 'dawg'
  };
  var q = querystring.encode(obj);
  jsonp(ENDPOINT + '?'
+ q, function (err, data) { if (err) throw err; t.deepEqual(data, obj); }); }); test('timeout', function (t) { t.plan(1); var obj = { delay: 5 // time in seconds after which data should be returned }; var q = querystring.encode(obj); jsonp(ENDPOINT + '?' + q, { timeout: 3000 }, function (err, data) { t.ok(err instanceof Error); }); }); test('named callback', function (t) { t.plan(1); var obj = { beep: 'boop', yo: 'dawg' }; var q = querystring.encode(obj); jsonp(ENDPOINT + '?' + q, { name: 'namedCb' }, function (err, data) { if (err) throw err; t.deepEqual(data, obj); }); });

原始碼分析

/**
 * Module dependencies
 */

var debug = require('debug')('jsonp');

/**
 * Module exports.
 */

module.exports = jsonp;

/**
 * Callback index.
 */

var count = 0;

/**
 * Noop function.
 */

function noop(){}

/**
 * JSONP handler
 *
 * Options:
 *  - param {String} qs parameter (`callback`)
 *  - prefix {String} qs parameter (`__jp`)
 *  - name {String} qs parameter (`prefix` + incr)
 *  - timeout {Number} how long after a timeout error is emitted (`60000`)
 *
 * @param {String} url
 * @param {Object|Function} optional options / callback
 * @param {Function} optional callback
 */

function jsonp(url, opts, fn){
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }
  if (!opts) opts = {};

  var prefix = opts.prefix || '__jp';

  // use the callback name that was passed if one was provided.
  // otherwise generate a unique name by incrementing our counter.
  var id = opts.name || (prefix + (count++));

  var param = opts.param || 'callback';
  var timeout = null != opts.timeout ? opts.timeout : 60000;
  var enc = encodeURIComponent;
  var target = document.getElementsByTagName('script')[0] || document.head;
  var script;
  var timer;


  if (timeout) {
    timer = setTimeout(function(){
      cleanup();
      if (fn) fn(new Error('Timeout'));
    }, timeout);
  }

  function cleanup(){
    if (script.parentNode) script.parentNode.removeChild(script);
    window[id] = noop;
    if (timer) clearTimeout(timer);
  }

  function cancel(){
    if (window[id]) {
      cleanup();
    }
  }

  window[id] = function(data){
    debug('jsonp got', data);
    cleanup();
    if (fn) fn(null, data);
  };

  // add qs component
  url += (~url.indexOf('?') ? '&' : '?') + param + '=' + enc(id);
  url = url.replace('?&', '?');

  debug('jsonp req "%s"', url);

  // create script
  script = document.createElement('script');
  script.src = url;
  target.parentNode.insertBefore(script, target);

  return cancel;
}