Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace our Promise polyfill with the one from core-js #9054

Merged
merged 1 commit into from
Oct 21, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
324 changes: 1 addition & 323 deletions src/shared/compatibility.js
Original file line number Diff line number Diff line change
Expand Up @@ -862,333 +862,11 @@ PDFJS.compatibilityChecked = true;
};
})();

/**
* Polyfill for Promises:
* The following promise implementation tries to generally implement the
* Promise/A+ spec. Some notable differences from other promise libraries are:
* - There currently isn't a separate deferred and promise object.
* - Unhandled rejections eventually show an error if they aren't handled.
*
* Based off of the work in:
* https://bugzilla.mozilla.org/show_bug.cgi?id=810490
*/
(function checkPromise() {
if (globalScope.Promise) {
// Promises existing in the DOM/Worker, checking presence of all/resolve
if (typeof globalScope.Promise.all !== 'function') {
globalScope.Promise.all = function (iterable) {
var count = 0, results = [], resolve, reject;
var promise = new globalScope.Promise(function (resolve_, reject_) {
resolve = resolve_;
reject = reject_;
});
iterable.forEach(function (p, i) {
count++;
p.then(function (result) {
results[i] = result;
count--;
if (count === 0) {
resolve(results);
}
}, reject);
});
if (count === 0) {
resolve(results);
}
return promise;
};
}
if (typeof globalScope.Promise.resolve !== 'function') {
globalScope.Promise.resolve = function (value) {
return new globalScope.Promise(function (resolve) {
resolve(value);
});
};
}
if (typeof globalScope.Promise.reject !== 'function') {
globalScope.Promise.reject = function (reason) {
return new globalScope.Promise(function (resolve, reject) {
reject(reason);
});
};
}
if (typeof globalScope.Promise.prototype.catch !== 'function') {
globalScope.Promise.prototype.catch = function (onReject) {
return globalScope.Promise.prototype.then(undefined, onReject);
};
}
return;
}

var STATUS_PENDING = 0;
var STATUS_RESOLVED = 1;
var STATUS_REJECTED = 2;

// In an attempt to avoid silent exceptions, unhandled rejections are
// tracked and if they aren't handled in a certain amount of time an
// error is logged.
var REJECTION_TIMEOUT = 500;

var HandlerManager = {
handlers: [],
running: false,
unhandledRejections: [],
pendingRejectionCheck: false,

scheduleHandlers: function scheduleHandlers(promise) {
if (promise._status === STATUS_PENDING) {
return;
}

this.handlers = this.handlers.concat(promise._handlers);
promise._handlers = [];

if (this.running) {
return;
}
this.running = true;

setTimeout(this.runHandlers.bind(this), 0);
},

runHandlers: function runHandlers() {
var RUN_TIMEOUT = 1; // ms
var timeoutAt = Date.now() + RUN_TIMEOUT;
while (this.handlers.length > 0) {
var handler = this.handlers.shift();

var nextStatus = handler.thisPromise._status;
var nextValue = handler.thisPromise._value;

try {
if (nextStatus === STATUS_RESOLVED) {
if (typeof handler.onResolve === 'function') {
nextValue = handler.onResolve(nextValue);
}
} else if (typeof handler.onReject === 'function') {
nextValue = handler.onReject(nextValue);
nextStatus = STATUS_RESOLVED;

if (handler.thisPromise._unhandledRejection) {
this.removeUnhandeledRejection(handler.thisPromise);
}
}
} catch (ex) {
nextStatus = STATUS_REJECTED;
nextValue = ex;
}

handler.nextPromise._updateStatus(nextStatus, nextValue);
if (Date.now() >= timeoutAt) {
break;
}
}

if (this.handlers.length > 0) {
setTimeout(this.runHandlers.bind(this), 0);
return;
}

this.running = false;
},

addUnhandledRejection: function addUnhandledRejection(promise) {
this.unhandledRejections.push({
promise,
time: Date.now(),
});
this.scheduleRejectionCheck();
},

removeUnhandeledRejection: function removeUnhandeledRejection(promise) {
promise._unhandledRejection = false;
for (var i = 0; i < this.unhandledRejections.length; i++) {
if (this.unhandledRejections[i].promise === promise) {
this.unhandledRejections.splice(i);
i--;
}
}
},

scheduleRejectionCheck: function scheduleRejectionCheck() {
if (this.pendingRejectionCheck) {
return;
}
this.pendingRejectionCheck = true;
setTimeout(() => {
this.pendingRejectionCheck = false;
var now = Date.now();
for (var i = 0; i < this.unhandledRejections.length; i++) {
if (now - this.unhandledRejections[i].time > REJECTION_TIMEOUT) {
var unhandled = this.unhandledRejections[i].promise._value;
var msg = 'Unhandled rejection: ' + unhandled;
if (unhandled.stack) {
msg += '\n' + unhandled.stack;
}
// Raising and catching the error, so debugger can break on it.
try {
throw new Error(msg);
} catch (_) {
console.warn(msg);
}
this.unhandledRejections.splice(i);
i--;
}
}
if (this.unhandledRejections.length) {
this.scheduleRejectionCheck();
}
}, REJECTION_TIMEOUT);
},
};

var Promise = function Promise(resolver) {
this._status = STATUS_PENDING;
this._handlers = [];
try {
resolver.call(this, this._resolve.bind(this), this._reject.bind(this));
} catch (e) {
this._reject(e);
}
};

/**
* Builds a promise that is resolved when all the passed in promises are
* resolved.
* @param {array} promises array of data and/or promises to wait for.
* @return {Promise} New dependent promise.
*/
Promise.all = function Promise_all(promises) {
var resolveAll, rejectAll;
var deferred = new Promise(function (resolve, reject) {
resolveAll = resolve;
rejectAll = reject;
});
var unresolved = promises.length;
var results = [];
if (unresolved === 0) {
resolveAll(results);
return deferred;
}
function reject(reason) {
if (deferred._status === STATUS_REJECTED) {
return;
}
results = [];
rejectAll(reason);
}
for (var i = 0, ii = promises.length; i < ii; ++i) {
var promise = promises[i];
var resolve = (function(i) {
return function(value) {
if (deferred._status === STATUS_REJECTED) {
return;
}
results[i] = value;
unresolved--;
if (unresolved === 0) {
resolveAll(results);
}
};
})(i);
if (Promise.isPromise(promise)) {
promise.then(resolve, reject);
} else {
resolve(promise);
}
}
return deferred;
};

/**
* Checks if the value is likely a promise (has a 'then' function).
* @return {boolean} true if value is thenable
*/
Promise.isPromise = function Promise_isPromise(value) {
return value && typeof value.then === 'function';
};

/**
* Creates resolved promise
* @param value resolve value
* @returns {Promise}
*/
Promise.resolve = function Promise_resolve(value) {
return new Promise(function (resolve) {
resolve(value);
});
};

/**
* Creates rejected promise
* @param reason rejection value
* @returns {Promise}
*/
Promise.reject = function Promise_reject(reason) {
return new Promise(function (resolve, reject) {
reject(reason);
});
};

Promise.prototype = {
_status: null,
_value: null,
_handlers: null,
_unhandledRejection: null,

_updateStatus: function Promise__updateStatus(status, value) {
if (this._status === STATUS_RESOLVED ||
this._status === STATUS_REJECTED) {
return;
}

if (status === STATUS_RESOLVED &&
Promise.isPromise(value)) {
value.then(this._updateStatus.bind(this, STATUS_RESOLVED),
this._updateStatus.bind(this, STATUS_REJECTED));
return;
}

this._status = status;
this._value = value;

if (status === STATUS_REJECTED && this._handlers.length === 0) {
this._unhandledRejection = true;
HandlerManager.addUnhandledRejection(this);
}

HandlerManager.scheduleHandlers(this);
},

_resolve: function Promise_resolve(value) {
this._updateStatus(STATUS_RESOLVED, value);
},

_reject: function Promise_reject(reason) {
this._updateStatus(STATUS_REJECTED, reason);
},

then: function Promise_then(onResolve, onReject) {
var nextPromise = new Promise(function (resolve, reject) {
this.resolve = resolve;
this.reject = reject;
});
this._handlers.push({
thisPromise: this,
onResolve,
onReject,
nextPromise,
});
HandlerManager.scheduleHandlers(this);
return nextPromise;
},

catch: function Promise_catch(onReject) {
return this.then(undefined, onReject);
},
};

globalScope.Promise = Promise;
globalScope.Promise = require('core-js/fn/promise');
})();

(function checkWeakMap() {
Expand Down