目 录CONTENT

文章目录

Ping.js源码分析及遇到的问题

hideonheart
2023-10-15 / 0 评论 / 0 点赞 / 17 阅读 / 0 字

Ping.js源码分析及遇到的问题

背景:现在在做的一个业务系统新功能——证件扫描,其中使用到了webRTC中的getUserMedia方法,在页面点击扫描按钮的时候,手机浏览器的授权弹窗一直没有弹出来。最开始怀疑是不同手机和浏览器的底层请求权限性能问题,后面连接电脑调试的时候发现进度条一直在加载中,然后查看网络,有一个图片资源一直卡在加载过程中。最后发现就是一个ping.js测试网络联通性的代码导致的。

来看下ping.js的原理,以下是github上的源码

/**
 * Creates a Ping instance.
 * @returns {Ping}
 * @constructor
 */
var Ping = function(opt) {
    this.opt = opt || {};
    this.favicon = this.opt.favicon || "/favicon.ico";
    this.timeout = this.opt.timeout || 0;
    this.logError = this.opt.logError || false;
};

/**
 * Pings source and triggers a callback when completed.
 * @param {string} source Source of the website or server, including protocol and port.
 * @param {Function} callback Callback function to trigger when completed. Returns error and ping value.
 * @returns {Promise|undefined} A promise that both resolves and rejects to the ping value. Or undefined if the browser does not support Promise.
 */
Ping.prototype.ping = function(source, callback) {
    var promise, resolve, reject;
    if (typeof Promise !== "undefined") {
        promise = new Promise(function(_resolve, _reject) {
            resolve = _resolve;
            reject = _reject;
        });
    }

    var self = this;
    self.wasSuccess = false;
    self.img = new Image();
    self.img.onload = onload;
    self.img.onerror = onerror;

    var timer;
    var start = new Date();

    function onload(e) {
        self.wasSuccess = true;
        pingCheck.call(self, e);
    }

    function onerror(e) {
        self.wasSuccess = false;
        pingCheck.call(self, e);
    }

    if (self.timeout) {
        timer = setTimeout(function() {
            pingCheck.call(self, undefined);
    }, self.timeout); }


    /**
     * Times ping and triggers callback.
     */
    function pingCheck() {
        if (timer) { clearTimeout(timer); }
        var pong = new Date() - start;

        if (!callback) {
            if (promise) {
                return this.wasSuccess ? resolve(pong) : reject(pong);
            } else {
                throw new Error("Promise is not supported by your browser. Use callback instead.");
            }
        } else if (typeof callback === "function") {
            // When operating in timeout mode, the timeout callback doesn't pass [event] as e.
            // Notice [this] instead of [self], since .call() was used with context
            if (!this.wasSuccess) {
                if (self.logError) { console.error("error loading resource"); }
                if (promise) { reject(pong); }
                return callback("error", pong);
            }
            if (promise) { resolve(pong); }
            return callback(null, pong);
        } else {
            throw new Error("Callback is not a function.");
        }
    }

    self.img.src = source + self.favicon + "?" + (+new Date()); // Trigger image load with cache buster
    return promise;
};

if (typeof exports !== "undefined") {
    if (typeof module !== "undefined" && module.exports) {
        module.exports = Ping;
    }
} else {
    window.Ping = Ping;
}

源码十分简单,核心方法ping中 new 了一个 Image,然后给img设置srcsource + self.favicon + "?" + (+new Date()),这个source就是要测试网站联通性的一个网络地址,favicon是浏览器tab栏上面显示的那个小图标,后面带上时间戳保证不使用本地缓存。判断网站联通性的原理就是利用图片加载没有跨域问题这个点,通过onload方法判断是否可以正常加载图片。如果可以正常加载就是可以访问该网站,否则就是不能。

遇到的问题

图片加载不像网络请求一样可以abort,如果无法联通该网站的情况下,发送出去的图片请求是取消不了的。在我使用webRTC的getUserMedia方法时候无法及时响应权限弹窗。详细问题请查看WebRTC之 getUserMedia 与 document.readyState

解决

超时的时候将 img.src设置为空即可

    if (self.timeout) {
        timer = setTimeout(function() {
            pingCheck.call(self, undefined);
            self.img.src = ''
    }, self.timeout); }
0

评论区