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:

  1. Promise is a class.
  2. The constructor takes a function fn, which takes two arguments, one for success and one for failure
  3. Promise objects have a then method, one for successful callbacks and one for failed callbacks
  4. The THEN method supports chained calls
  5. Then returns a new Promise object
  6. Promise objects have a catch method that catches errors thrown by reject
  7. Promise static method all, race;
  8. 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