preface
Promises are a solution to asynchronous programming: syntactically, promises are an object from which to retrieve messages for asynchronous operations; In its original sense, it is a promise that will give you results over time. Promise has three states: pending, fulfiled, and Rejected. Once the state has changed, it will never change. Once a Promise instance is created, it executes immediately.
Write promise implementations that comply with the promiseA+ specification
Before implementing it, take A look at the Promise A Plus specification
1. Create the Promise constructor
Here, the most basic function of promise is realized first: immediately execute after promise is created; Execute the corresponding function when then; The catch error immediately becomes reject.
// Promise has only one argument, called executorfunction Promise(executor) {
let self = this;
self.status = 'pending'; // wait state self.value = undefined; // The default success value self.err = undefined; // Default failed valuefunction resolve(value) {
if (self.status === 'pending') {
self.status = 'resolved'; self.value = value; }}function reject(err) {
if (self.status === 'pending') {
self.status = 'rejected'; self.err = err; }} reject {reject (resolve, reject) {reject (resolve, reject); } catch (error) {reject(error); }} // Define on prototypethenInstance method promise.prototype. then =function (onFulfilled, onRejected) {
let self = this;
if (self.status === 'resolved') {
onFulfilled(self.value);
}
if (self.status === 'rejected') { onRejected(self.err); }}Copy the code
Let’s test our Promise here
This is where the basic functionality is implemented, as promised is a solution for asynchronous programming; Let’s add asynchronous logic to run it:
2. Promise is invoked asynchronously
We all know that asynchronous code doesn’t execute immediately. It’s not resolved, it’s not Rejected, it’s pending.
In the previous state determination, a pending state was lost.
When the status is pending, onFulfilled and onRejected will be stored in the array first. When the status changes, we will go through the number group again to make the functions in it execute successively and see the code.
Field () onFulfiled(), onRejected(
function Promise(resolver) {
let self = this;
self.status = 'pending'; // wait state self.value = undefined; // The default success value self.err = undefined; // Default failed value self.onresolvedCallbacks = []; / / storethenSuccessful callback self.onrejectedCallbacks = []; / / storethenFailed callbackfunction resolve(value) {
if (self.status === 'pending') {
self.status = 'resolved'; self.value = value; Self. OnResolvedCallbacks. ForEach (fn = > {/ / call the resolve, executed in sequence in the array function fn (); }}})function reject(err) {
if (self.status === 'pending') {
self.status = 'rejected'; self.err = err; self.onRejectedCallbacks.forEach(fn=>{ fn(); Resolver (resolve, reject); } Catch (error) {reject(error)}}Copy the code
(2) Add pending judgment to then method
Promise.prototype.then = function (onFulfilled, onRejected) {
let self = this;
if (self.status === 'resolved') {
onFulfilled(self.value);
}
if (self.status === 'rejected') {
onRejected(self.err);
}
if(self.status==='pending') {/ / not resolved at this time, also didn't rejectd self. OnResolvedCallbacks. Push (() = > {onFulfilled (self. Value); }); self.onRejectedCallbacks.push(()=>{ onRejected(self.err); }}})Copy the code
Now, asynchronous logic
After 1s, the execution is successful, isn’t it amazing? Look at the following:
3. Promise chain call
(1) The specification states that “THEN” can be called multiple times in the same promise.
(2) jquery can implement chained calls by returning this, but promise cannot return this. It returns a new promise instance (note, not the original promise instance).
Create a new promise2 in then with a Promise for each state pack
This returns a new promise2, which also calls resolve or reject; Resolve (x) : resolve(x) : resolve(x) : resolve(x) : resolve(x) : resolve(x
(1) x may be a promise;
(2) May be an object or method;
(3) It may also be an ordinary value.
You need a method to handle x
3.1 Process the return values of onFulfilled and onRejected
Introduce a resolvePromise(promise2, x, resolve, reject); The four parameters here are
- Promise2 is the new promise that we return
- X is the return value of the previous THEN
- Resolve is the successful method
- Reject is a method of failure
It’s important to note that some people may write promises that both succeed and fail, and if both are called the first one is ignored. Add a call to resolvePromise(promise2, x, resolve, reject) :
function resolvePromise(promise2, x, resolve, reject) {
if(promise2 === x) {//promise2 and x cannot be the samereturn reject(new TypeError('Circular reference'))}letcalled; // Whether the call succeeded or failed // here the type of x is judgedif(x ! == null && (typeof x ==='object' || typeof x === 'function'// Determine if x is a promise if x is an object and x'sthenThe method is a function and we think of it as a promiselet then = x.then;
if (typeof then= = ='function') {
then.call(x, function (y) {
if (called) return
called = trueResolvePromise (promise2, y, resolve, reject)}function(err) {// Failedif (called) return
called = truereject(err); })}else {
resolve(x)
}
} catch (e) {
if (called) return
called = true; reject(e); }}else{// specify a common value 1 resolve(x); }}Copy the code
Make some changes to the previous code accordingly
4. Value penetration problem
If nothing is passed in then, the value will pass through to the last call;
This will need to write a default function for onFulfilled and onRejected in then
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
return value;
}
onRejected = typeof onRejected === 'function' ? onRejected : function(err) { throw err; // We need to throw an error herereturnErr, otherwise the next call to success state}Copy the code
5. Asynchronous implementation of THEN
According to the specification, all the onFulfilled and onRejected must be performed asynchronously
Use resolve as an example to write setTimeout():
6. Please defer to me
To use a promise, we all need to start with new promise (), for example:
function read() {
let fs = require('fs');
let promise = new Promise(function(resolve,reject){
fs.readFile('./1.txt'.'utf8'.function(err,data){
if(err) reject(err); resolve(data); })});return promise
}
Copy the code
In Promise, it provides us with a syntax-sugar promise.defer. With promise.defer, just write:
function read() {
let defer = Promise.defer()
require('fs').readFile('.//1.txt'.'utf8'.function (err, data) {
if(err) defer.reject(err);
defer.resolve(data);
})
return defer.promise;
}
Copy the code
To do this, add a defer method to our Promise:
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd
}
Copy the code
Here, we basically implemented a fairly complete promise; Of course, Promise also has a lot of static methods, as well as the asynchronous history of JS, which can be discussed next time. Complete code:
// Promise has only one argument, called executorfunction Promise(executor) {
let self = this;
self.status = 'pending'; // wait state self.value = undefined; // The default success value self.err = undefined; // Default failed value self.onresolvedCallbacks = []; / / storethenSuccessful callback self.onrejectedCallbacks = []; / / storethenFailed callbackfunction resolve(value) {
if (self.status === 'pending') {
self.status = 'resolved';
self.value = value;
self.onResolvedCallbacks.forEach(function(fn) { fn(); }); }}function reject(err) {
if (self.status === 'pending') {
self.status = 'rejected';
self.err = err;
self.onRejectedCallbacks.forEach(function(fn) { fn(); }); }} reject {reject (resolve, reject) {reject (resolve, reject); } catch (error) {reject(error); }}function resolvePromise(promise2, x, resolve, reject) {
if(promise2 === x) {//promise2 and x cannot be the samereturn reject(new TypeError('Circular reference'))}letcalled; // Whether the call succeeded or failed // here the type of x is judgedif(x ! == null && (typeof x ==='object' || typeof x === 'function'// Determine if x is a promise if x is an object and x'sthenThe method is a function and we think of it as a promiselet then = x.then;
if (typeof then= = ='function') {
then.call(x, function (y) {
if (called) return
called = trueResolvePromise (promise2, y, resolve, reject)}function(err) {// Failedif (called) return
called = truereject(err); })}else {
resolve(x)
}
} catch (e) {
if (called) return
called = true; reject(e); }}else{// specify a common value 1 resolve(x); }} // defined on prototypethenInstance method promise.prototype. then =function (onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
return value;
}
onRejected = typeof onRejected === 'function' ? onRejected : function(err) { throw err; // We need to throw an error herereturnErr, otherwise the next call to success state}let self = this;
letpromise2; // Return promiseif (self.status === 'resolved') {
promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
letx = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); }})})}if (self.status === 'rejected') {
promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
letx = onRejected(self.err); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); }})})} // When calledthenIt may not succeed or failif (self.status === 'pending') {
promise2 = new Promise(function(resolve, reject) {/ / at this point no resolve no reject self. OnResolvedCallbacks. Push (function () {
setTimeout(function () {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
});
self.onRejectedCallbacks.push(function () {
setTimeout(function () {
try {
letx = onRejected(self.err); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); }})}); })}return promise2;
}
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd
}
module.exports = Promise;
Copy the code
7. Promise test
npm i -g promises-aplus-tests
promises-aplus-tests Promise.js
Copy the code