Introduction: There are many JS implementations of Promise on the web, but they are basically based on ES5, es6 has a more concise syntax, why not try?
Phase 1:
Only chain calls are supported and no other apis are supported
const pending = 'pending'
const resolved = 'resolved'
const rejected = 'rejected'Class MyPromise {constructor (cb) {this._data = null // Error = null // Failure message this.SuccessSubs = [] // Resolve callback queue this.failSubs = [] // Reject callback queueif (typeof cb === 'function') {cb(this.resolve.bind(this), this.reject. Bind (this))else if(cb && typeof cb ! = ='function') {
return new TypeError('MyPromise constructor must be a function'}} resolve (_data) {// There should be two cases here // 1 callthenThe promise.resolve () method is called directly after the // 2 subscription.if (this._status === pending) {
this._status = resolved
this._data = _data
this.successSubs.forEach(fn => fn())
}
}
reject (_error) {
if(this._status === pending) { this._status = rejected this._error = _error this.failSubs.forEach(fn => fn()) } } ResolvePromise (x,resolve, reject) {// If MyPromise is returnedif (x instanceof MyPromise) {
x.then(data => {
resolve(data)
},
error => {
reject(error)
})
} else{// Common value: execute directlythenResolve, the last of the new Promise methods returnedthenResolve (x)}}then(success, fail) {// chain call the last onethenCall the previous onethenReturn an instance of promiselet p = new MyPromise((resolve, reject) => {
if(this._status === pending) {// The promise instance is still called pendingthenMethod, increase subscribersif (success && typeof success === 'function') {this.successSubs.push(() => {try {// user callthen// 1 non-PROMISE object:returnWhat is passed as a parameter to the nextthenWhat // 2 Promise object: Wait for the result of the user's promise before executing the next functionletThis. ResolvePromise (x, resolve, reject)} catch (e) {reject(e)}})}if (fail && typeof fail === 'function') {
this.failSubs.push(() => {
try {
let x = fail(this._error)
this.resolvePromise(x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
} else if (this._status === resolved) {
try {
let x = success(this._data)
this.resolvePromise(x, resolve, reject)
} catch (e) {
reject(e)
}
} else if(this._status === rejected) {
try {
let x = fail(this._error)
this.resolvePromise(x, resolve, reject)
} catch (e) {
reject(e)
}
}
})
return p
}
}
Copy the code
Stage 2:
Implement other apis:
Promise. Resolve () and Promise. Reject ()
Both of these methods return a promise object in a non-pending state, along with a method that is unique to the Promise class but not the instance. This works perfectly with the es6 class Static method.
. Static resolve (value) {// call promise. resolve (value) {returnNew MyPromise(resolve => {resolve(value)}) {reject static reject(reason) {return new MyPromise((undefined, reject) => {
reject(reason)
})
}
...
Copy the code
Promise.all
Promise.all(iterable): The method returns an instance of a Promise that is resolved when all promises in iterable arguments are “resolved” or when the arguments do not contain the Promise; If a promise fails (Rejected), the instance calls back (reject) because of the result of the first failed promise.
A value is iterable. We use Symbol. Iterator to determine whether a value is iterable. Of iteration 2 if all is normal (no reject), the instance can be resolved. 3. Resolve is an asynchronous iterable. If the iterable is iterable, resolve will be called. 4 Reject Reject only the reason of the current failure. Reject all the previous reasons. 5 Static private methods meet requirements
static all (iterable) {
if (iterable[Symbol.iterator]) {
return new MyPromise((resolve, reject) => {
let resolveArr = []
letLen = iterable.length // Check that all promises are completedfunction checkAll () {
if (resolveArr.length === len) {
resolve(resolveArr)
}
}
try {
for (letX of iterable) {// Each item can be a Promise value and other valuesif (x instanceof MyPromise) {
x.then(data => {
resolveArr.push(data)
checkAll()
}, reason => {
reject(reason)
})
} else {
resolveArr.push(x)
checkAll()
}
}
} catch (e) {
reject(e)
}
})
} else{// Not an iterable: throws an errorlet str = ({}).toString.call(iterable)
letReg = / ^ \ [object \ s ([a-z] [a-z] {8} 2) \] $/let matchArr = str.match(reg)
let msg = (matchArr && matchArr[1]) || str
throw new TypeError( msg + ': is not iterable')}}Copy the code
An example of testing MDN returns:
Iterable. Length === iterable. Length === iterable.
2 provides a key, which is used to mark the order. Currently, there are two options available, and only option 2 can be selected:
static all (iterable) {
if (iterable[Symbol.iterator]) {
return new MyPromise((resolve, reject) => {
let resolveArr = []
letLen = iterable.length // Check that all promises are completedfunction checkAll () {
if (resolveArr.length === len) {
resolve(resolveArr.sort((a, b) => {
return a.key - b.key
}).map(v => v.data))
}
}
try {
let key = -1
for (letX of iterable) {key++ // each item can be a Promise value and other valuesif (x instanceof MyPromise) {
x.then(data => {
resolveArr.push({data, key})
checkAll()
}, reason => {
reject(reason)
})
} else {
resolveArr.push({data:x, key})
checkAll()
}
}
} catch (e) {
reject(e)
}
})
} else{// Not an iterable: throws an errorlet str = ({}).toString.call(iterable)
letReg = / ^ \ [object \ s ([a-z] [a-z] {8} 2) \] $/let matchArr = str.match(reg)
let msg = (matchArr && matchArr[1]) || str
throw new TypeError( msg + ': is not iterable')}}Copy the code
Check it out:
Promise.race
The promise.race (iterable) method returns a Promise that is resolved or rejected once a Promise in the iterator is resolved or rejected.
The RACE method is similar to the ALL method, but relatively simpler:
static race (iterable) {
if (iterable[Symbol.iterator]) {
return new MyPromise((resolve, reject) => {
try {
for (letX of iterable) {// Each item can be a Promise value and other valuesif (x instanceof MyPromise) {
x.then(data => {
resolve(data)
}, reason => {
reject(reason)
})
} else {
resolve(x)
}
}
} catch (e) {
reject(e)
}
})
} else{// Not an iterable: throws an errorlet str = ({}).toString.call(iterable)
letReg = / ^ \ [object \ s ([a-z] [a-z] {8} 2) \] $/let matchArr = str.match(reg)
let msg = (matchArr && matchArr[1]) || str
throw new TypeError( msg + ': is not iterable')}}Copy the code
Test the MDN example