Last active
August 27, 2022 14:59
-
-
Save Rich-Harris/11010768 to your computer and use it in GitHub Desktop.
ES6 Promise polyfill
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
( function ( global ) { | |
'use strict'; | |
var Promise, PENDING = {}, FULFILLED = {}, REJECTED = {}; | |
if ( typeof global.Promise === 'function' ) { | |
Promise = global.Promise; | |
} else { | |
Promise = function ( callback ) { | |
var fulfilledHandlers = [], | |
rejectedHandlers = [], | |
state = PENDING, | |
result, | |
dispatchHandlers, | |
makeResolver, | |
fulfil, | |
reject, | |
promise; | |
makeResolver = function ( newState ) { | |
return function ( value ) { | |
if ( state !== PENDING ) { | |
return; | |
} | |
result = value; | |
state = newState; | |
dispatchHandlers = makeDispatcher( ( state === FULFILLED ? fulfilledHandlers : rejectedHandlers ), result ); | |
// dispatch onFulfilled and onRejected handlers asynchronously | |
wait( dispatchHandlers ); | |
}; | |
}; | |
fulfil = makeResolver( FULFILLED ); | |
reject = makeResolver( REJECTED ); | |
callback( fulfil, reject ); | |
promise = { | |
// `then()` returns a Promise - 2.2.7 | |
then: function ( onFulfilled, onRejected ) { | |
var promise2 = new Promise( function ( fulfil, reject ) { | |
var processResolutionHandler = function ( handler, handlers, forward ) { | |
// 2.2.1.1 | |
if ( typeof handler === 'function' ) { | |
handlers.push( function ( p1result ) { | |
var x; | |
try { | |
x = handler( p1result ); | |
resolve( promise2, x, fulfil, reject ); | |
} catch ( err ) { | |
reject( err ); | |
} | |
}); | |
} else { | |
// Forward the result of promise1 to promise2, if resolution handlers | |
// are not given | |
handlers.push( forward ); | |
} | |
}; | |
// 2.2 | |
processResolutionHandler( onFulfilled, fulfilledHandlers, fulfil ); | |
processResolutionHandler( onRejected, rejectedHandlers, reject ); | |
if ( state !== PENDING ) { | |
// If the promise has resolved already, dispatch the appropriate handlers asynchronously | |
wait( dispatchHandlers ); | |
} | |
}); | |
return promise2; | |
} | |
}; | |
promise[ 'catch' ] = function ( onRejected ) { | |
return this.then( null, onRejected ); | |
}; | |
return promise; | |
}; | |
Promise.all = function ( promises ) { | |
return new Promise( function ( fulfil, reject ) { | |
var result = [], pending, i, processPromise; | |
if ( !promises.length ) { | |
fulfil( result ); | |
return; | |
} | |
processPromise = function ( i ) { | |
promises[i].then( function ( value ) { | |
result[i] = value; | |
if ( !--pending ) { | |
fulfil( result ); | |
} | |
}, reject ); | |
}; | |
pending = i = promises.length; | |
while ( i-- ) { | |
processPromise( i ); | |
} | |
}); | |
}; | |
Promise.race = function ( promises ) { | |
return new Promise( function ( fulfil, reject ) { | |
promises.forEach( function ( promise ) { | |
promise.then( fulfil, reject ); | |
}); | |
}); | |
}; | |
Promise.resolve = function ( value ) { | |
return new Promise( function ( fulfil ) { | |
fulfil( value ); | |
}); | |
}; | |
Promise.reject = function ( reason ) { | |
return new Promise( function ( fulfil, reject ) { | |
reject( reason ); | |
}); | |
}; | |
} | |
// TODO use MutationObservers or something to simulate setImmediate | |
function wait ( callback ) { | |
setTimeout( callback, 0 ); | |
} | |
function makeDispatcher ( handlers, result ) { | |
return function () { | |
var handler; | |
while ( handler = handlers.shift() ) { | |
handler( result ); | |
} | |
}; | |
} | |
function resolve ( promise, x, fulfil, reject ) { | |
// Promise Resolution Procedure | |
var then; | |
// 2.3.1 | |
if ( x === promise ) { | |
throw new TypeError( 'A promise\'s fulfillment handler cannot return the same promise' ); | |
} | |
// 2.3.2 | |
if ( x instanceof Promise ) { | |
x.then( fulfil, reject ); | |
} | |
// 2.3.3 | |
else if ( x && ( typeof x === 'object' || typeof x === 'function' ) ) { | |
try { | |
then = x.then; // 2.3.3.1 | |
} catch ( e ) { | |
reject( e ); // 2.3.3.2 | |
return; | |
} | |
// 2.3.3.3 | |
if ( typeof then === 'function' ) { | |
var called, resolvePromise, rejectPromise; | |
resolvePromise = function ( y ) { | |
if ( called ) { | |
return; | |
} | |
called = true; | |
resolve( promise, y, fulfil, reject ); | |
}; | |
rejectPromise = function ( r ) { | |
if ( called ) { | |
return; | |
} | |
called = true; | |
reject( r ); | |
}; | |
try { | |
then.call( x, resolvePromise, rejectPromise ); | |
} catch ( e ) { | |
if ( !called ) { // 2.3.3.3.4.1 | |
reject( e ); // 2.3.3.3.4.2 | |
called = true; | |
return; | |
} | |
} | |
} | |
else { | |
fulfil( x ); | |
} | |
} | |
else { | |
fulfil( x ); | |
} | |
} | |
// export as node module | |
if ( typeof module !== 'undefined' && module.exports ) { | |
module.exports = Promise; | |
} | |
// or as AMD module | |
else if ( typeof define === 'function' && define.amd ) { | |
define( function () { return Promise; }); | |
} | |
global.Promise = Promise; | |
}( typeof window !== 'undefined' ? window : this )); |
A much lighter weight Promise.All alternative vowAll
vowAll is for xhr requests and isn't an alternative to Promise.all.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
FYI, one issue I noticed is your implementation of
Promise.all
doesn't wrap non-promise values in aPromise
like nativePromise.all
does. For example:I believe you could fix this by wrapping
promises[i]
in aPromise.resolve
call; e.g.:Also, this is really nit-picky, but I'd spell "fulfil" as "fulfill" to be consistent.
EDIT: Just realized who authored this gist; thanks for
rollup
! It really is wonderful.