Promise usage
Promise is a new asynchronous syntax in ES6 that addresses the issue of callback regions
new Promise((resolve, reject) => {
setTimeout(()=>{
resolve(1);
}, 2000);
}).then(val => {
console.log('val',val);
return new Promise(resolve => {
setTimeout(()=>{
resolve(2);
}, 2000)
})
}).then(val => {
console.log('val',val);
})
// 1
// 2
Copy the code
Implement state switching
- A Promise instance has three states: Pending, depressing, and Rejected
- A Promise instance can be constructed by passing in an execution function that takes resolve and reject to change the Promise state. Once the Promise state is changed, it cannot be changed
- The execution of the function is synchronized when the Promise instance is created
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class Promise2 {
constructor (executor) {
this.status = 'pending';
this.value = null;
this.reason = null;
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
}
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
}
}
executor(resolve, reject);
}
}
let p = new Promise2((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 2000)
})
Copy the code
Implement then asynchronous execution
A Promise instance can call the then method and pass in a callback:
If the Promise instance is a fulfilled state when then is called, the incoming callback will be executed asynchronously immediately. If the Promise is pending when then is called, the incoming callback will wait until resolve before executing asynchronously
Example 1: Promise is in ffulFilled state
let p = new Promise((resolve, reject) => { console.log(1); resolve(2); console.log(3); }) p.then(val => { console.log(val); }) // execute first (resolve)Copy the code
Example 2: Promise is a pending state
let p = new Promise((resolve, reject) => { setTimeout(() => { resolve(1); }) }) p.then(val => { console.log(val); }) // Run 1 second before printing 1 because you need to wait for resolve to finishCopy the code
Execute a callback in the resolve queue asynchronously. Determine the state of the instance before executing the callback in the THEN queue.
const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; class Promise2 { constructor (executor) { this.status = 'pending'; this.value = null; this.reason = null; this.onFulfilledCallbacks = []; // Successful callback queue this.onrejectedCallbacks = []; Const resolve = (value) => {if (this.status === PENDING) {this.status == PENDING; this.value = value; / / the callback asynchronous execution queue setTimeout (() = > {the console. The log (' onFulfilledCallbacks, enclosing onFulfilledCallbacks); this.onFulfilledCallbacks.forEach(callbacks => { callbacks(this.value); }) }) } } const reject = (reason) => { if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; / / the callback asynchronous execution queue setTimeout (() = > {this. OnRejectedcallbacks. ForEach (callbacks = > {callbacks (enclosing a tiny); }) }) } } executor(resolve, reject); } then (onFulfilled, // This. Status === depressing) {setTimeout(()=>{onFulfilled(this. Value)); // This. If (this.staus === REJECTED) {setTimeout(() => {onRejected(this.reason); })} / / status in the pending the if (this. The status = = = pending) {this. OnFulfilledCallbacks. Push (onFulfilled); / / store the callback function enclosing onRejectedcallbacks. Push (onRejected); // Store callback function}}} let p = new Promise2((resolve, reject) => {console.log(1); resolve(2); console.log(3); }) p.then(val => { console.log(val); Let p = new Promise2((resolve, reject) => {setTimeout(() => {resolve(1); console.log(2); }) }) p.then(val => { console.log(val); }) // Print 2 1Copy the code
Resolve Promise instance
The value of resolve may also be a Promise instance, in which case the value of resolve itself will be used
let p = new Promise((resolve, reject) => { const p2 = new Promise((resolve2, reject2) => setTimeout(() => resolve2(1), 2000)); resolve(p2); }) p.then(value => { console.log('val',value); // Output 2} after 2 seconds)Copy the code
Therefore, it is necessary to determine the resolve function of Promise1. If the Promise instance is followed by a THEN, the resolve of Promise1 is passed as a callback to the THEN of Promise2
Const resolve = (value) => {// if (value instanceof this.constructor) {// if (value instanceof this.constructor) {// Then (resolve, reject) return; } if (this.status === PENDING) { this.status = FULFILLED; this.value = value; / / asynchronous execution in the queue callback setTimeout (() = > {this. OnFulfilledCallbacks. ForEach (callbacks = > {callbacks (enclosing value); }) }) } } let p = new Promise2((resolve, reject) => { const p2 = new Promise2((resolve2, reject2) => setTimeout(() => resolve2(1), 2000)); resolve(p2); }) p.then(value => { console.log('val',value); // Output 2} after 2 seconds)Copy the code
Implement chain calls
Then can be called chained, and the return value of the previous THEN callback is the return value of the previous THEN callback if it is not a Promise instance. If the Promise instance is not a Promise instance, the return value of the next THEN callback is the return value of the previous THEN callback. Is the resolved value of the Promise instance returned by the previous THEN callback.
Let p = new Promise((resolve, reject) => {setTimeout(() => resolve(1), 2000)}) Console. log('val', val); return new Promise(resolve => setTimeout(() => resolve(2), 2000)); }). Then (val => {// If it is not a Promise instance, the value of the next THEN callback is the return value of the previous THEN callback console.log('val',val); return 3 }).then(val => { console.log('val',val); }) // Prints 1 after 2 seconds and 2 and 3 together after 2 secondsCopy the code
Since it can be called chained, the then method itself must return a Promise instance. Is the returned Promise instance itself? The answer is obvious: no. If a Promise’s then method returns the Promise itself, the resolve method is called when new a Promise, because the Promise’s state cannot be changed again, then all of the following THEN methods can only perform successful callbacks, with no error handling. This is clearly not in line with the Promise specification and the original intention of designing promises.
So the then method returns a new Promise instance
then (onFulfilled, onRejected) { return new this.constructor((resolve, This. Status === depressing) {setTimeout(()=>{// If this. Status === depressing) {setTimeout(()=>{// If there is a mistake in the syntax, there is a mistake in the syntax onFulfilled(this.value); resolve(callbakcValue); }catch (e) { reject(e); If (this.staus === REJECTED) {setTimeout(() => {try {let callBackValue = onRejected(this.reason); resolve(callBackValue); }catch (e) { reject(e); }})} / / status in the pending the if (this. The status = = = pending) {this. OnFulfilledCallbacks. Push (() = > {try {let callBackValue = onFulfilled(this.value); resolve(callBackValue); }catch (e) { reject(e) } }); / / store the callback function enclosing onRejectedcallbacks. Push (() = > {try {let callBackValue = onRejected (enclosing a tiny); resolve(callBackValue); }catch (e) { reject(e) } }); // Store the callback function}}); } let p = new Promise2((resolve, reject) => {setTimeout(() => resolve(1), 2000)}) Console. log('val', val); return new Promise2(resolve => setTimeout(() => resolve(2), 2000)); }). Then (val => {// If it is not a Promise instance, the value of the next THEN callback is the return value of the previous THEN callback console.log('val',val); return 3 }).then(val => { console.log('val',val); }) // Prints 1 after 2 seconds and 2 and 3 together after 2 secondsCopy the code
Implementation of catch – resolve – reject
Calss Promise2 {static resolve (value) {// If (value instanceof this) {return value; } return new this((reslove, reject) => { reslove(value); Return new this((reslove, reject) => {reject(reason); }) } constructor (executor) {... }, then (onFulfilled, onRejected) {... }, then(null, onRejected) {return this. Then (null, onRejected)}} reject) => { reject(1); }) p.catch(e => { console.log('e', e); }) // promise.resolve static method let p = promise.resolve (1); // promise. reject let p = Promise2. Reject (1)Copy the code
To achieve all
calss Promise2 { static resolve (value) {... } static reject (reason) {... } static all (promises) { return new this((resolve, reject) => { let promiseNum = promises.length; let resolvedNum = 0; let resolvedValues = [...promises]; for (let i = 0; i < promiseNum; i++) { this.resolve(promises[i]) .then(val => { resolvedNum++; resolvedValues[i] = val; If (resolvedNum === promiseNum) {// All promises are resolved (resolvedValues); } }, reason => { reject(reason); }); }}); } constructor (executor) {... }, then (onFulfilled, onRejected) {... }, catch (onRejected) {... } } let p = Promise2.all([ new Promise2((resolve, reject) => setTimeout(() => resolve(1), 1000)), new Promise2((resolve, reject) => setTimeout(() => resolve(2), 2000)), new Promise2((resolve, reject) => setTimeout(() => resolve(3), 3000)), ]); p.then(val => { console.log('val', val); }); Print 1, 2, 3 after 6 secondsCopy the code
Realize the race
calss Promise2 { static resolve (value) {... } static reject (reason) {... } static all (promises) {... } static race (promises) { return new this((resolve, reject) => { let length = promises.length; for (let i = 0; i < length; i++) { this.resolve(promises[i]) .then( val => { resolve(val); }, reason => { reject(reason); }); }}); } constructor (executor) {... }, then (onFulfilled, onRejected) {... }, catch (onRejected) {... } } let p = Promise2.race([ new Promise2((resolve, reject) => setTimeout(() => resolve(1), 1000)), new Promise2((resolve, reject) => setTimeout(() => resolve(2), 2000)), new Promise2((resolve, reject) => setTimeout(() => resolve(3), 3000)), ]); p.then(val => { console.log('val', val); }); // Prints the fastest return result 1Copy the code
Macro and micro tasks
A macroTask is a task that is scheduled to the next event loop. A microTask is a task that is scheduled to the end of the current event loop and may be executed earlier than a macroTask. The Promise standard doesn’t specify which asynchrony to use in promises, but both Node and browser implementations use microtasks. Js is a single thread of all the main thread synchronization task execution first, then execute the micro task queue procedures, finally execute macro task queue, adhering to the principle of first-in, first-out.
The macro task apis include: setTimeout(),setInterval(),setImmediate(nodeApi),requestAnimationFrame(), various IO operations, and network requests.
Microtask apis include: process.nexttick (nodeApi),MutationObserver(). Promise.then catch finally
Replace the setTimeout macro task in the Promise above with a microtask
Let nextTick = (function () {let callbacks = []; let counter = 1; let node = document.createElement('div'); function handler () { let copy = callbacks.slice(); callbacks = []; copy.forEach(cb => cb()); } let obServer = new MutationObserver(handler); obServer.observe(node, { childList: true, }); return function (cb) { callbacks.push(cb); // Trigger MutationObserver counter = (counter + 1) % 2; node.innerHTML = counter; }; }) (); const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; Class Promise2 {// promise. resolve static resolve (value) {// resolve If (value instanceof this) {return value; } return new this((reslove, reject) => { reslove(value); }); } // promise. reject method static reject(reason) {// reject(reason) => {reject(reason); }); } // promise. static all (Promise) {return new this((resolve, reject) => {let promiseNum = promise.length; let resolvedNum = 0; let resolvedValues = [...promises]; for (let i = 0; i < promiseNum; i++) { this.resolve(promises[i]) .then(val => { resolvedNum++; resolvedValues[i] = val; If (resolvedNum === promiseNum) {// All promises are resolved (resolvedValues); } }, reason => { reject(reason); }); }}); } // Promise. Race method static race (promises) {return new this((resolve, reject) => {let length = promises. for (let i = 0; i < length; i++) { this.resolve(promises[i]) .then( val => { resolve(val); }, reason => { reject(reason); }); }}); } constructor (executor) { this.status = 'pending'; this.value = null; this.reason = null; this.onFulfilledCallbacks = []; // Successful callback queue this.onrejectedCallbacks = []; Const resolve = (value) => {// If (value instanceof this.constructor) {// Const resolve = (value) => {// If (value instanceof this.constructor) {// Then (resolve, reject); then(resolve, reject) return; } if (this.status === PENDING) { this.status = FULFILLED; this.value = value; / / asynchronous execution in the queue callback nextTick (() = > {this. OnFulfilledCallbacks. ForEach (callbacks = > {callbacks (enclosing value); }); }); }}; const reject = (reason) => { if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; / / the callback asynchronous execution queue nextTick (() = > {this. OnRejectedcallbacks. ForEach (callbacks = > {callbacks (enclosing a tiny); }); }); }}; executor(resolve, reject); } then (onFulfilled, onRejected) { return new this.constructor((resolve, This. Status === very depressing) {nextTick(() => {// This. Status === very depressing) {nextTick(() => {// This onFulfilled(this.value); resolve(callbakcValue); } catch (e) { reject(e); }}); If (this.status === REJECTED) {nextTick(() => {try {let callBackValue = onRejected(this.reason); resolve(callBackValue); } catch (e) { reject(e); }}); } / / status in the pending the if (this. The status = = = pending) {this. OnFulfilledCallbacks. Push (() = > {try {let callBackValue = onFulfilled(this.value); resolve(callBackValue); } catch (e) { reject(e); }}); / / store the callback function enclosing onRejectedcallbacks. Push (() = > {try {let callBackValue = onRejected (enclosing a tiny); resolve(callBackValue); } catch (e) { reject(e); }}); // Store the callback function}}); } catch (onRejected) { return this.then(null, onRejected); }}Copy the code
Because the Promise itself uses microtasks, we need to replace the macro task steTimeout in the implementation code above with the implemented microtask method. In this case, 1 will be printed and then wait for 1 second. After 2 is printed, 3 will be printed at the same time. When macro task is used, 2 will be printed and then 3 will be printed successively, and there will be an execution interval.
The information on the Internet varies in depth, and I am also in the process of learning the summary, if you find mistakes, welcome to comment out ~