I Promise you/I Promise you
Promise is an important concept in JS asynchronous programming, which abstracts objects asynchronously and is one of the more popular Javascript asynchronous programming solutions at present.
Promise/A+ is the smallest specification of Promise. including
- Promise state - then method - Promise resolution layerCopy the code
There is only one THEN method, no catch, race, all, etc. ECMAscript 6 Promise is one of the criteria that meets the Promise/A+ standard.
- Miss ruan ES6
- Promise/A + specification
- Promise/A+ standard Chinese translation
- Promise/A+ Test
Two. Concrete implementation
- PromiseA frameworkHere’s an example:
let p = new Promise(function(resolve, reject) {
resolve('200');
})
p.then(data= > {
console.log(data);
}, err => {
console.log('err', err)
})
/ / 200
Copy the code
Analyze:
1. New Promise Returns a Promise object that accepts an 'executor' executor function and calls the function immediately. 2. Executor accepts two arguments resolve and reject, which are also functions. 3. The Promise instance has a state that defaults to 'pending' and changes to 'resolved' after the executor calls resolve. The operator calls reject, and the instance state changes to 'Rejected'. 4. Once the state of the Promise instance changes, it cannot be modified. 5. Promise instances all have 'then' methods. The then method takes two parameters. 'onResolved' successfully callback, 'onRejected' 'onResolved' in 'then' will be executed when 'executor' calls' resolve ', and 'onRejected', the second argument to the 'then' method, will be executed when 'Executor' calls' reject '.Copy the code
Implement:
// Promise three states
var PENDING = 'pending';
var RESOLVED = 'resolved';
var REJECTED = 'rejected';
function PromiseA (executor) {
// Save this to prevent this from pointing to something else
var _this = this;
// Initialize the promise value
_this.data = undefined;
// Initialize the state of the promise
_this.status = PENDING;
function resolve(value) {
// Change the corresponding state and promise values in pending
if(_this.status === PENDING) { _this.status = RESOLVED; _this.data = value; }}function reject(reason) {
// Change the corresponding state and promise values in pending
if(_this.status === PENDING) {
_this.status = REJECTED;
_this.data = reason;
}
}
executor(resolve, reject);
}
PromiseA.prototype.then = function(onResolved, onRejected) {
var _this = this;
// The state is a success state, and the success callback is executed immediately, passing in its value
if(_this.status === RESOLVED) {
onResolved(_this.data);
}
// The state is a failed state, and the failed callback is executed immediately, passing in its value
if(_this.status === REJECTED) { onRejected(_this.data); }}module.exports = PromiseA;
Copy the code
- Asynchronously executed, then method called multiple timesHere’s an example:
let p = new PromiseTest(function(resolve, reject) {
setTimeout((a)= > {
resolve('200');
}, 1000)
})
p.then(data= > {
console.log(1, data)
}, err => {
console.log('err', err)
})
p.then(data= > {
console.log(2, data)
}, err => {
console.log('err', err)
})
/ / 1, '200'
/ / 2, '200'
Copy the code
Analyze:
The result will be printed one second later, meaning that the failure and success callbacks of the THEN method are triggered after the asynchronous execution of the promise, so the state of the promise is not success or failure when the THEN method is called. The successful or failed callback functions are saved and executed after the asynchronous execution completes. The then method can be called multiple times, so arrays are needed to save
Implement:
function PromiseA (executor) {
// ...
// Save successful and failed callback functions
_this.resolvedCallbacks = [];
_this.rejectedCallbacks = [];
// Call success function
function resolve(value) {
// Change the corresponding state and promise values in pending
if(_this.status === PENDING) {
_this.status = RESOLVED;
_this.data = value;
_this.resolvedCallbacks.forEach(function(fn) { fn(); }); }}// Call the failed function
function reject(reason) {
// Change the corresponding state and promise values in pending
if(_this.status === PENDING) {
_this.status = REJECTED;
_this.data = reason;
_this.rejectedCallbacks.forEach(function(fn) {
fn();
});
}
}
}
PromiseA.prototype.then = function(onResolved, onRejected) {
// ...
// The state is waiting, and the callback function is saved
if(_this.status === PENDING) {
_this.resolvedCallbacks.push(function() {
onResolved(_this.data);
})
_this.rejectedCallbacks.push(function() { onRejected(_this.data); }}})Copy the code
- Error trappingHere’s an example:
let p = new PromiseA((resolve, reject) = > {throw new Error('error')});
p.then(data= > {
console.log(data);
}, err => {
console.log('err', err)
})
// err Error: error
Copy the code
Break it down: When a Promise goes wrong, it changes directly to the failed state and passes on the reason for the failure. Exception handling is done directly to the executor function, reject if anything goes wrong.
Implement:
function PromiseA (executor) {
// ...
try {
executor(resolve, reject);
} catch(reason) { reject(reason); }}Copy the code
- The then method is called chainedHere’s an example:
let p = new Promise(function(resolve, reject) {
resolve('200');
})
p.then(data= > {
console.log(1, data)
throw Error('oooo')
}, err => {
console.log('1err', err)
}).then(data= > {
console.log(2, data);
}, err => {
console.log('2err', err)
}).then(data= > {
console.log(3, data)
}, err => {
console.log('3err', err)
})
console.log('start');
// start
/ / 1 '200'
// 2err Error: oooo
// 3 undefined
Copy the code
Analyze:
- Promise is an asynchronous execution function. So the first print
start
, the use ofsetTimeout
Ensure the order of execution. - Promise instance call
then
Method, returns a new Promise instance, - The result of a successful or failed Promise execution is passed to the next Promise instance
then
methodsonResolved
oronRejected
Parameters for the callback. - The Promise instance is invoked chained
then
When any one of themthen
Execution error occurs when the chain is called nextthen
Will execute the wrong callback, - The return value is undefined
undefined
, which executes a successful callback, as above3 undefined
.
Implement:
function PromiseA (executor) {
// ...
function resolve(value) {
// Change the corresponding state and promise values in pending
setTimeout(function() {
if(_this.status === PENDING) {
_this.status = RESOLVED;
_this.data = value;
_this.resolvedCallbacks.forEach(function(fn) { fn(); }); }})}function reject(reason) {
// Change the corresponding state and promise values in pending
setTimeout(function() {
if(_this.status === PENDING) {
_this.status = REJECTED;
_this.data = reason;
_this.rejectedCallbacks.forEach(function(fn) {
fn();
});
}
})
}
}
PromiseA.prototype.then = function(onResolved, onRejected) {
var _this = this;
var promise2;
promise2 = new PromiseA(function(resolve, reject) {
// execute asynchronously to ensure the order of calls
setTimeout(function() {
// The state is a success state, and the success callback is executed immediately, passing in its value
if(_this.status === RESOLVED) {
// the then method executes exception processing, rejecting the error
try {
var x = onResolved(_this.data);
resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason)
}
}
// The state is a failed state, and the failed callback is executed immediately, passing in its value
if(_this.status === REJECTED) {
var x = onRejected(_this.data);
resolvePromise(promise2, x, resolve, reject);
}
// The state is waiting, and the callback function is saved
if(_this.status === PENDING) {
_this.resolvedCallbacks.push(function() {
var x = onResolved(_this.data);
resolvePromise(promise2, x, resolve, reject);
})
_this.rejectedCallbacks.push(function() {
varx = onRejected(_this.data); resolvePromise(promise2, x, resolve, reject); })}})})return promise2;
}
function resolvePromise(promise2, x, resolve, reject) {
resolve(x);
}
Copy the code
- The value of the penetrationHere’s an example:
let p = new Promise(function(resolve, reject) {
resolve('200');
})
p.then()
.then(null, err => {
console.log('1err', err)
})
.then(data= > {
console.log(data)
}, err => {
console.log('2err', err)
})
/ / '200'
Copy the code
Analyze: When the previous THEN does not pass the callback parameter or the parameter is null, you need to pass the value to the next THEN method. The then method has two optional parameters onResolved and onRejected. Leave the then parameter blank and let the value pass through.
Implement:
PromiseA.prototype.then = function(onResolved, onRejected) {
onResolved = typeof onResolved === 'function' ? onResolved : function(value) {return value};
onRejected = typeof onRejected === 'function' ? onREjected : function(reason) {throw reason};
// ...
}
Copy the code
- A circular referenceHere’s an example:
let p = new Promise(function(resolve, reject) {
resolve('200');
})
var p2 = p.then((a)= > {
return p2;
})
p2.then((a)= > {
console.log(1)
}, err => {
console.log('err1', err)
})
// err1 TypeError: Chaining cycle detected for promise
Copy the code
If p’s then method calls itself, it will generate a loopback
Implement:
function resolvePromise(promise2, x, resolve, reject) {
if(promise2 === x) {
return reject(new TypeError('Circular reference')); }}// ...
Copy the code
- ResolvePromise function implementationThe Promise/A+ specification involved in resolvePromise
- Call each Promise instance
then
The new Promise instance, called promise2, will be returnedthen
The value returned by the callback is calledx
- Promise2 does not work
x
For the same object, waiting for itself, circular reference. - if
x
Is an object or function and is notnull
, just to getx
Then method of, ifx
Is the object that preventsx
Is through theObject.defineProperty
addthen
Property, and add get and set listeners. If an exception is thrown in a listener, it needs to be caught.x.then
Phi is a function, let’s sayx
Is an instance of Promise, implemented directlyx
的then
Method, the success of the execution of thepromise2
If you succeed, you failpromise2
Failure, ifx.then
If it is not a functionx
Is a normal value and is called directlypromise2
的resolve
Method will bex
If the condition is not met, the return value is a normal value and is executed directlypromise2
的resolve
And pass x as a parameter; - If every time
x
的then
Method, the argument passed in the callback is once again an instance of Promise, repeating over and over again, recursivelyresolvePromise
parsing - In the recursive process, the inner and outer layers are called at the same time
resolve
和reject
Should declare an identity variablecalled
Make a judgment call to avoid this
- Call each Promise instance
Implement:
function resolvePromise(promise2, x, resolve, reject) {
var then;
// To avoid multiple calls
var called = false;
if(promise2 === x) {
return reject(new TypeError('Loop callback'));
}
// if x is a normal value (not object or function), resolve
if(x ! = =null && (typeof x === 'object' || typeof x === 'function')) {// Each promise has a then method, which uses _THEN to prevent errors and a try catch
try {
then = x.then;
if(typeof then === 'function') {
// Make sure this refers to x
then.call(x, function(y) {
if(called) return;
called = true;
return resolvePromise(promise2, y, resolve, reject);
}, function(e) {
if(called) return;
called = true;
returnreject(e); })}else{ resolve(x); }}catch (err) {
if(called) return;
called = true; reject(err); }}else{ resolve(x); }}Copy the code
Three. Test it out
Promises – aplus-Tests: Promises – aplus-Tests: Promises – Aplus-Tests: Promises – Aplus-Tests: Promises – Aplus-Tests
PromiseA.deferred = PromiseA.defer = function() {
var dfd = {}
dfd.promise = new PromiseA(function(resolve, reject) {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
Copy the code
4. Extended methods
- Catch methodHere’s an example:
let p = new Promise(function(resolve, reject) {
resolve('200');
})
p.then(data= > {
throw new Error('eeee');
}, err => {
console.log('err', err)
}).catch(err= > {
console.log(err)
})
// Error eeee
Copy the code
Implement:
PromiseA.prototype.catch = function(onRejected) {
return this.then(null, onRejected);
}
Copy the code
- Resolve methodHere’s an example:
let p = Promise.resolve('200');
p.then(data= > {
console.log(data)
}, err => {
console.log('err', err)
})
/ / 200
Copy the code
Implement:
Promise.prototype.resolve = function(value) {
return new Promise(function(resolve, reject) { resolve(value); })}Copy the code
- Reject methodHere’s an example:
let p = Promise.reject('eeee');
p.then(data= > {
console.log(data)
}, err => {
console.log('err', err)
})
// err eeee
Copy the code
Implement:
Promise.reject = function(reason) {
return new Promise(function(resolve, reject) {
reject(reason)
})
}
Copy the code
- Race methodHere’s an example:
let p1 = new Promise(function(resolve, reject) {
setTimeout((a)= > {
resolve(1)},1000)})let p2 = new Promise(function(resolve, reject) {
setTimeout((a)= > {
resolve(2)},2000)})let p3 = new Promise(function(resolve, reject) {
setTimeout((a)= > {
resolve(3)},3000)})let p = Promise.race([p1, p2, p3])
p.then(data= > {
console.log(data)
}, err => {
console.log('err', err)
})
/ / 1
Copy the code
Implement:
Promise.race = function(promises) {
return new Promise(function(resolve, reject) {
promises.forEach(function(promise) {
promise.then(resolve, reject)
})
})
}
Copy the code
Five. All the code
// Promise three states
var PENDING = 'pending';
var RESOLVED = 'resolved';
var REJECTED = 'rejected';
/** * * @param {function} executor */
function PromiseA (executor) {
// Save this to prevent this from pointing to something else
var _this = this;
// Initialize the promise value
_this.data = undefined;
// Initialize the state of the promise
_this.status = PENDING;
// Save successful and failed callback functions
_this.resolvedCallbacks = [];
_this.rejectedCallbacks = [];
// Call success function
function resolve(value) {
// Change the corresponding state and promise values in pending
setTimeout(function() {
if(_this.status === PENDING) {
_this.status = RESOLVED;
_this.data = value;
_this.resolvedCallbacks.forEach(function(fn) { fn(); }); }})}// Call the failed function
function reject(reason) {
// Change the corresponding state and promise values in pending
setTimeout(function() {
if(_this.status === PENDING) {
_this.status = REJECTED;
_this.data = reason;
_this.rejectedCallbacks.forEach(function(fn) { fn(); }); }})}New PromiseA((resolve, reject) => {throw new Error(' Error ')})
try {
executor(resolve, reject);
} catch (reason) {
reject(reason)
}
}
/** ** @param {promise} promise2 then Returns a new promise object * @param {*} x Return value of onResolved in promise * @param {*} resolve Resolve method of promise2 * @param {*} reject Method of promise2 */
function resolvePromise(promise2, x, resolve, reject) {
var then;
// To avoid multiple calls
var called = false;
if(promise2 === x) {
return reject(new TypeError('Loop callback'));
}
// if x is a normal value (not object or function), resolve
if(x ! = =null && (typeof x === 'object' || typeof x === 'function')) {// Each promise has a then method, which uses _THEN to prevent errors and a try catch
try {
then = x.then;
if(typeof then === 'function') {
// Make sure this refers to x
then.call(x, function(y) {
if(called) return;
called = true;
return resolvePromise(promise2, y, resolve, reject);
}, function(e) {
if(called) return;
called = true;
returnreject(e); })}else{ resolve(x); }}catch (err) {
if(called) return;
called = true; reject(err); }}else{ resolve(x); }}/** * @param {function} onResolved error */
PromiseA.prototype.then = function(onResolved, onRejected) {
var _this = this;
var promise2;
// Value penetration
onResolved = typeof onResolved === 'function' ? onResolved : function(value) {return value};
onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {throw reason};
return promise2 = new PromiseA(function(resolve, reject) {
// execute asynchronously to ensure the order of calls
setTimeout(function() {
// The state is a success state, and the success callback is executed immediately, passing in its value
if(_this.status === RESOLVED) {
// For the inside
try {
var x = onResolved(_this.data);
resolvePromise(promise2, x, resolve, reject);
} catch(reason) { reject(reason); }}// The state is a failed state, and the failed callback is executed immediately, passing in its value
if(_this.status === REJECTED) {
try {
var x = onRejected(_this.data);
resolvePromise(promise2, x, resolve, reject);
} catch(reason) { reject(reason); }}// The state is waiting, and the callback function is saved
if(_this.status === PENDING) {
_this.resolvedCallbacks.push(function() {
try {
var x = onResolved(_this.data);
resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
})
_this.rejectedCallbacks.push(function() {
try {
var x = onRejected(_this.data);
resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason)
}
})
}
})
})
}
PromiseA.prototype.catch = function(onRejected) {
return this.then(null, onRejected);
}
PromiseA.resolve = function(value) {
return new PromiseA(function(resolve, reject) {
resolve(value);
})
}
PromiseA.reject = function(reason) {
return new PromiseA(function(resolve, reject) {
reject(reason)
})
}
PromiseA.race = function(promises) {
return new PromiseA(function(resolve, reject) {
promises.forEach(function(promise) {
promise.then(resolve, reject)
})
})
}
// Use Promises -aplus-tests
PromiseA.deferred = PromiseA.defer = function() {
var dfd = {}
dfd.promise = new PromiseA(function(resolve, reject) {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = PromiseA;
Copy the code
Six Reference.
- Miss ruan ES6
- Promise/A + specification
- Promise/A+ standard Chinese translation
- Promise/A+ Test
- Parse the Promise internals