Skip to content

Instantly share code, notes, and snippets.

@Jessidhia
Last active June 29, 2016 04:12
Show Gist options
  • Save Jessidhia/df60aaad1be5dba6402ed00fa15de5dc to your computer and use it in GitHub Desktop.
Save Jessidhia/df60aaad1be5dba6402ed00fa15de5dc to your computer and use it in GitHub Desktop.
import $ from 'jquery'
// jQuery Deferreds don't have a prototype, so patching them is more complicated
// than the native Promises.
const origDeferred = $.Deferred
const sym = module.hot && module.hot.data
? module.hot.data
: Symbol('deferred-finally')
// throws -- should only be called inside a .then handler
function execFinally (func, resolveValue, shouldThrow) {
const p = func()
// If p is Promise-like
if (typeof p.then === 'function') {
// Restores the original resolveValue, but only if p doesn't reject
return p.then(postFinally)
}
return postFinally()
function postFinally () {
if (shouldThrow) {
throw resolveValue
}
return resolveValue
}
}
const finallyPrototype = {
finally (func) {
func = () => func()
// Use .then instead of .always to ensure it behaves like Promise.finally
// Rejection in the "catch" branch must always be propagated
// Could use Promise.resolve(this).finally, but that would change the type of the result :(
return this.then(
(v) => execFinally(func, v, false),
(e) => execFinally(func, e, true)
)
}
}
function patchDeferred (obj) {
if (obj[sym]) {
return obj
}
// $.Deferred basically calls self.promise(self) to add promise methods to self
const origPromise = obj.promise
// Patch .promise to run the original .promise, and patch the resulting promise as well
// Because this modifies the parameter, it will work even when called in void context (like inside $.ajax)
obj.promise = function promise (...args) {
return patchDeferred(origPromise.call(this, ...args))
}
if (!obj.finally) {
Object.assign(obj, finallyPrototype)
}
obj[sym] = true
return obj
}
// Replace $.Deferred with a version that patches itself before running the
// callback or returning.
//
// Tested with jQuery 3.0.0.
$.Deferred = function Deferred (init = function () {}) {
return origDeferred.call(this, /* @this */ function deferred () {
patchDeferred(this)
init.call(this, this)
})
}
if (module.hot) {
module.hot.accept()
module.hot.dispose(() => {
$.Deferred = origDeferred
return sym
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment