preface
Those of you who read handwritten promises know more or less how they work and what they do. So here is not a detailed introduction of the historical background and role of Promise, a brief introduction to the use of various methods, and their own handwritten Promise process, as a learning record.
Method of use
Write a Promise test file. Older Versions of Node already support the Promise script, so run this file directly to debug.
// Promise.js
const fs = require("fs")
function asyncReadFile() {
return new Promise((resolve, reject) = > {
fs.readFile('./demo.txt'.(err, data) = > {
if(err) {
reject(err)
} else {
resolve(data)
}
})
})
}
function eventError() {
return new Promise((resolve, reject) = > {
fs.readFile('./demo.txt'.(err, data) = > {
reject("Error content")})})}function _setTimeout() {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve("setTimeout")},1500)})}function sync() {
return new Promise((resolve, reject) = > {
resolve(88)})}// then accept the successful method
asyncReadFile()
.then(ret= > console.log(ret.toString())) // Print the content of dome.txt
// then accepts the wrong method
eventError()
.then(null.err= > console.log(err)) // Error content
/ / catch method
eventError()
.catch(err= > console.log(err)) // Error content
/ / resolve synchronization
sync()
.then(err= > console.log(err)) // Error content
// A chained call to THEN that takes the return value of the previous THEN and is a new Promise object
_setTimeout
.then(ret= > console.log(ret)) // Print setTimeout after 1.5
.then(ret= > { console.log(ret); return 'aaa' }) // undefined
.then(ret= > console.log(ret)) // aaa
// all static method. Accept an array of Promise instances. Execute then after all instances of asynchrony have completed, and return the corresponding result.
Promise
.all([asyncReadFile(), _setTimeout()])
.then(ret= > {
console.log(ret) // ['demo. TXT file contents ', 'setTimeout']
})
// race static method. Accept an array of Promise instances. Where the instance executes then after asynchrony completes, returning its result.
Promise
.race([asyncReadFile(), _setTimeout()])
.then(ret= > {
console.log(ret)
})
Promise.resolve(3).then(ret= > console.log(ret))
// Then calls and returns a Promise object. After a Promise is executed, then is executed. The then argument is resolve/reject. This is not verified in this handwriting
// Print the order 'demo.txt file contents' -> (1.5s later...) 'setTimeout'
asyncReadFile().then(ret= > {
console.log(ret.toString())
return _setTimeout()
})
.then(ret= > {
console.log(ret)
})
/ / holes
new Promise((resolve) = > {
setTimeout(() = > resolve(1), 500)
setTimeout(() = > resolve(2), 1500)
})
.then(ret= > console.log(ret))
Copy the code
To summarize the basic structure of promises:
- Promise is a class.
- The constructor takes a function fn, which takes two arguments, one for success and one for failure
- Promise objects have a then method, one for successful callbacks and one for failed callbacks
- The THEN method supports chained calls
- Then returns a new Promise object
- Promise objects have a catch method that catches errors thrown by reject
- Promise static method all, race;
- The Promise state can only go from Pending to Reslove or Reject
Promise is a class
class Promise {
}
Copy the code
The constructor takes the first argument to a function that exposes two functions as arguments, the first called on success and the second called on failure
class Promise { constructor(fn) { const resolve = () => { console.log('sucess! ') } const reject = () => { console.log('reject!!! ') } fn(resolve, reject) } }Copy the code
Implement the basic structure and then asynchronous call — publish subscribe
Combining the above features, we implement a simple Promise
class _Promise { constructor(executor) { this.status = 'pending' // resolved rejected this.value = null this.reason = Null this.onResolveCallbacks = [] // successful this.onRejectedCallbacks = [] // failed const resolve = function(value) { / / a successful call resolve function enclosing status = 'resolved'. This value = value if (this. OnResolveCallbacks. Length = = = 0) return This. OnResolveCallbacks. ForEach (fn = > fn (value) this.)} const reject = function (reject) {/ / reject the function called failure console.log(this) this.status = 'rejected' this.reason = reject if(this.onRejectedCallbacks.length === 0) { console.warn('UnhandledPromiseRejectionWarning:', this.reason) console.warn('UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().') return } this.onRejectedCallbacks.forEach(fn => Bind (this), reject. Bind (this))} then(onpity, onRejected) {// This is a pity. Push the handler into the container if (this. The status = = = 'pending') {onFulfilled && enclosing onResolveCallbacks. Push (onFulfilled) onRejected && this.onRejectedCallbacks.push(onRejected) } if(this.status === 'resolved') { onFulfilled && onFulfilled(this.value) } If (this.status === 'rejected') {onRejected && onRejected(this.reason)}} Catch (onRejected) {// if(this.status === 'pending') { this.onRejectedCallbacks.push(onRejected) } if(this.status === 'rejected' || this.status === 'resolved') { onRejected(this.reason) } } } const fs = require("fs") function asyncReadFile() { return new _Promise((resolve, reject) => { fs.readFile('./demo.txt', (err, data) => { if(err) { reject(err) } else { resolve(data) } }) }) } asyncReadFile() .then((res) => { console.log(res, '-')})Copy the code
The above implementation of the basic methods, but there are many problems, such as no chain calls, etc… Executor (resolve.bind(this), reject. Bind (this)) this object needs to be bound, otherwise reslove/reject will not get this object when exposed
Implement synchronous call to THEN — vulnerability 1
The problem with this implementation is that when reslove/ Reject is not in an asynchronous function, then’s two arguments will not be put into the subscribe container. When new _Promise is called, reslove is executed synchronously, so it is published before it can be subscribed. My solution is to add a setTimeout
constructor(executor) { const resolve = function(value) { setTimeout(() => { this.status = 'resolved' this.value = value if(this.onResolveCallbacks.length === 0) return this.onResolveCallbacks.forEach(fn => fn(this.value)) }, 0) } const reject = function(reject) { setTimeout(() => { this.status = 'rejected' this.reason = reject if(this.onRejectedCallbacks.length === 0) { console.warn('UnhandledPromiseRejectionWarning:', this.reason) console.warn('UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().') return } this.onRejectedCallbacks.forEach(fn => fn(this.reason)) }, 0) } executor(resolve.bind(this), reject.bind(this)) }Copy the code
After testing, solve the synchronization can not get worth the situation.
Implement chain calls to THEN
The above is not yet implemented in the chain call, test will find an error.
then(onFulfilled, onRejected) { return new _Promise((resolve, reject) => { if(this.status === 'onFulfilled') { this.resolveArr.push(() => { const x = onFulfilled(this.value) resolve(x) }) } if(this.status === 'onRejected') { this.rejectArr.push(() => { const x = onRejected(this.value) reject(x) }) } if(this.status === 'pending') { this.resolveArr.push(() => { const x = onFulfilled(this.value) resolve(x) }) this.rejectArr.push(() => { const x = onRejected(this.value) reject(x) }) } }) }Copy the code
Implementing catch methods
catch(onRejected) {
return this.then(null, onRejected)
}
Copy the code
Implement the static method resolve
_Promise.resolve = value => new _Promise(resolve => resolve(value))
Copy the code
Implement the static method Reject
_Promise.reject = value => new _Promise((resolve, reject) => reject(value))
Copy the code
Implement static method all
_Promise.all = function(promises) { // if(Object.prototype.toString.call(promises) ! == '[object Array]') return [] let res = [] return new this((resolve, reject) => { for (let i = 0; i < promises.length; i++) { let promiseItem = promises[i]; if(typeof promiseItem ! == 'object') promiseItem = this.resolve(promiseItem) promiseItem.then(ret => res.push(ret)) } resolve(res) }) }Copy the code
Implement the static method race
_Promise.race = promises =>
new _Promise((resolve, reject) =>
promises.forEach(pro => pro.then(resolve, reject))
)
Copy the code