Handwritten Promise source code

Abstract expression: Promise is a concrete expression of a new solution for asynchronous programming in JS: A Promise is a constructor that encapsulates an asynchronous operation and can retrieve its results

1. What is the meaning of promise and why are there promises?

  1. Before specifying a promise: You must specify a Promise before starting an asynchronous task: Start an asynchronous task => Return the Promie object => Bind a callback function to the Promise object (even after the asynchronous task has finished)
  2. Support for chained calls, which can solve the problem of callback hell. What is callback hell? The disadvantages of nested callbacks and asynchronous execution of external callbacks are nested callbacks performed by conditional callbacks hell? Not easy to read/not easy to troubleshoot the solution? Promise the ultimate chained call solution? async/await

2. What apis do Promise have?

  1. Promise constructor: Promise (excutor) {}
  2. (onResolved, onRejected) => {}
  3. (onRejected) => {}
  4. Promise.resolve method: (value) => {}
  5. Reject: (reason) => {}
  6. Promise. All method: (promises) => {}
  7. Promise. Race method: (promises) => {}

3.Promise

// Promise basic example

const promise = new Promise(function (resolve, reject) {
  // Make good on a promise

  // Resolve (100

  reject(new Error('promise rejected')) // Promise failed
})

promise.then(function (value) {
  // Even if there is no asynchronous operation, the callbacks passed in the then method are queued for the next round of execution
  console.log('resolved', value)
}, function (error) {
  console.log('rejected', error)
})

console.log('end')

Copy the code

4.Promise Use cases

// Promise style AJAX

function ajax (url) {
  Return a promise object
  return new Promise(function (resolve, reject) {
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.responseType = 'json'
    xhr.onload = function () {
      if (this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    xhr.send()
  })
}

ajax('/api/foo.json').then(function (res) {
  console.log(res)  // Call after the Ajax request succeeds
}, function (error) {
  console.log(error)  // Called after the Ajax request failed
})

Copy the code

5.Promise

One of the most common mistakes with promises is nesting:

function ajax(url) {
    return new Promise(function (resolve, reject) {
        var xhr = new XMLHttpRequest()
        xhr.open('GET', url)
        xhr.responseType = 'json'
        xhr.onload = function () {
            if (this.status === 200) {
                resolve(this.response)
            } else {
                reject(new Error(this.statusText))
            }
        }
        xhr.send()
    })
}
Copy the code
ajax('/api/urls.json').then(function (urls) {
    ajax(urls.users).then(function (users) {
        ajax(urls.users).then(function (users) {
            ajax(urls.users).then(function (users) {
                ajax(urls.users).then(function (users) {})})})})Copy the code

Nesting takes away the advantage of a Promise getting results from a state change, and still doesn’t get the result of whether or not one of the callbacks has completed.

6.Promise chain calls

// Promise chain calls

function ajax(url) {
    return new Promise(function (resolve, reject) {
        var xhr = new XMLHttpRequest()
        xhr.open('GET', url)
        xhr.responseType = 'json'
        xhr.onload = function () {
            if (this.status === 200) {
                resolve(this.response)
            } else {
                reject(new Error(this.statusText))
            }
        }
        xhr.send()
    })
}

// var promise = ajax('/api/users.json')

// var promise2 = promise.then(
// function onFulfilled (value) {
// console.log('onFulfilled', value)
/ /},
// function onRejected (error) {
// console.log('onRejected', error)
/ /}
// )

// console.log(promise2 === promise)

ajax('/api/users.json')
    .then(function (value) {
        console.log(1111)
        return ajax('/api/urls.json')})// => Promise
    .then(function (value) {
        console.log(2222)
        console.log(value)
        return ajax('/api/urls.json')})// => Promise
    .then(function (value) {
        console.log(3333)
        return ajax('/api/urls.json')})// => Promise
    .then(function (value) {
        console.log(4444)
        return 'foo'
    }) // => Promise
    .then(function (value) {
        console.log(5555)
        console.log(value)
    })

Copy the code

7.Promise exception handling

// Promise exception handling

function ajax (url) {
  return new Promise(function (resolve, reject) {
    // foo()
    // throw new Error()
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.responseType = 'json'
    xhr.onload = function () {
      if (this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    xhr.send()
  })
}

// ajax('/api/users11.json')
// .then(function onFulfilled (value) {
// console.log('onFulfilled', value)
// }, function onRejected (error) {
// console.log('onRejected', error)
/ /})

// Using catch to register failure callbacks is more common

// ajax('/api/users11.json')
// .then(function onFulfilled (value) {
// console.log('onFulfilled', value)
/ /})
// .catch(function onRejected (error) {
// console.log('onRejected', error)
/ /})

// If (onRejected) = if (onRejected)

// ajax('/api/users11.json')
// .then(function onFulfilled (value) {
// console.log('onFulfilled', value)
/ /})
// .then(undefined, function onRejected (error) {
// console.log('onRejected', error)
/ /})

// The onRejected object registered at the same time is only a failed callback for registering the current Promise object
// It can only catch exceptions for the current Promise object

// ajax('/api/users.json')
// .then(function onFulfilled (value) {
// console.log('onFulfilled', value)
// return ajax('/error-url')
// }, function onRejected (error) {
// console.log('onRejected', error)
/ /})

// Because any exception in the Promise chain is passed backward until caught
// The separate registration onRejected is equivalent to the whole Promise chain registration failure callback

ajax('/api/users.json')
  .then(function onFulfilled (value) {
    console.log('onFulfilled', value)
    return ajax('/error-url')})// => Promise {}
  // .catch(function onRejected (error) {
  // console.log('onRejected', error)
  // })

// Catch the Promise exception globally, similar to window.onError
window.addEventListener('unhandledrejection'.event= > {
  const { reason, promise } = event

  console.log(reason, promise)
  // Reason => Promise failure cause, usually an error object
  // Promise => The promise object where the exception occurred

  event.preventDefault()
}, false)

// Node.js uses the following method
// process.on('unhandledRejection', (reason, promise) => {
// console.log(reason, promise)
// // Reason => Promise Indicates the failure cause. It is usually an error object
// // PROMISE => The promise object where an exception occurs
// })

Copy the code

8.Promise static methods

// Use Promise static methods

function ajax (url) {
  return new Promise(function (resolve, reject) {
    // foo()
    // throw new Error()
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.responseType = 'json'
    xhr.onload = function () {
      if (this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    xhr.send()
  })
}

// Promise.resolve('foo')
// .then(function (value) {
// console.log(value)
/ /})

// new Promise(function (resolve, reject) {
// resolve('foo')
// })

// If a Promise object is passed, the promise. resolve method returns it as is

// var promise = ajax('/api/users.json')
// var promise2 = Promise.resolve(promise)
// console.log(promise === promise2)

// If we pass an object with a then method like Promise,
// Promise.resolve executes this object as a Promise

// Promise.resolve({
// then: function (onFulfilled, onRejected) {
// onFulfilled('foo')
/ /}
// })
// .then(function (value) {
// console.log(value)
// })

Reject any value passed in is used as a reason for the Promise to fail

// Promise.reject(new Error('rejected'))
// .catch(function (error) {
// console.log(error)
/ /})

Promise.reject('anything')
  .catch(function (error) {
    console.log(error)
  })

Copy the code

9.Promise parallel execution

// Promise is executed in parallel

function ajax (url) {
  return new Promise(function (resolve, reject) {
    // foo()
    // throw new Error()
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.responseType = 'json'
    xhr.onload = function () {
      if (this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    xhr.send()
  })
}

// ajax('/api/users.json')
// ajax('/api/posts.json')

// var promise = Promise.all([
// ajax('/api/users.json'),
// ajax('/api/posts.json')
// ])

// promise.then(function (values) {
// console.log(values)
// }).catch(function (error) {
// console.log(error)
// })

// ajax('/api/urls.json')
// .then(value => {
// const urls = Object.values(value)
// const tasks = urls.map(url => ajax(url))
// return Promise.all(tasks)
/ /})
// .then(values => {
// console.log(values)
/ /})

// Promise.race implements timeout control

const request = ajax('/api/posts.json')
const timeout = new Promise((resolve, reject) = > {
  setTimeout(() = > reject(new Error('timeout')), 500)})Promise.race([
  request,
  timeout
])
.then(value= > {
  console.log(value)
})
.catch(error= > {
  console.log(error)
})

Copy the code

10.Promise execution timing

Micro / / task

console.log('global start')

// The callback to setTimeout is a macro task
setTimeout(() = > {
  console.log('setTimeout')},0)

// Promise's callbacks are microtasks that are executed at the end of this round
Promise.resolve()
  .then(() = > {
    console.log('promise')
  })
  .then(() = > {
    console.log('promise 2')
  })
  .then(() = > {
    console.log('promise 3')})console.log('global end')

Copy the code
// Promise vs. Callback

function ajax (url, callback) {
  const executor = (resolve, reject) = > {
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.responseType = 'json'
    xhr.onload = () = > {
      if (xhr.status === 200) {
        resolve(xhr.response)
      } else {
        reject(new Error(xhr.statusText))
      }
    }
    xhr.send()
  }

  if (typeof callback === 'function') {
    // support callback
    executor(
      res= > callback(null, res),
      err= > callback(error)
    )
  }

  return new Promise(executor)
}

// ajax('/api/urls.json', (error, value) => {
// if (error) {
// return console.error(error)
/ /}
// console.log(value)
// })

// ajax('/api/urls.json')
// .then(value => {
// console.log(value)
/ /})
// .catch(error => {
// console.error(error)
/ /})

// Callback hell
ajax('/api/url1'.(error, value) = > {
  ajax('/api/url2'.(error, value) = > {
    ajax('/api/url3'.(error, value) = > {
      ajax('/api/url4'.(error, value) = >{})})})// Promise chain
ajax('/api/url1')
  .then(value= > {
    return ajax('ajax/url2')
  })
  .then(value= > {
    return ajax('ajax/url3')
  })
  .then(value= > {
    return ajax('ajax/url4')
  })
  .catch(error= > {
    console.error(error)
  })

// sync mode code
try {
  const value1 = ajax('/api/url1')
  console.log(value1)
  const value2 = ajax('/api/url2')
  console.log(value2)
  const value3 = ajax('/api/url3')
  console.log(value3)
  const value4 = ajax('/api/url4')
  console.log(value4)
} catch (e) {
  console.error(e)
}

Copy the code

11.Generator asynchronous scheme

// Generator function review

function * foo () {
  console.log('start')

  try {
    const res = yield 'foo'
    console.log(res)
  } catch (e) {
    console.log(e)
  }
}

const generator = foo()

const result = generator.next()
console.log(result)


// generator.next('bar')

generator.throw(new Error('Generator error'))

Copy the code
// The Generator is used with Promise

function ajax (url) {
  return new Promise((resolve, reject) = > {
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.responseType = 'json'
    xhr.onload = () = > {
      if (xhr.status === 200) {
        resolve(xhr.response)
      } else {
        reject(new Error(xhr.statusText))
      }
    }
    xhr.send()
  })
}

function * main () {
  try {
    const users = yield ajax('/api/users.json')
    console.log(users)

    const posts = yield ajax('/api/posts.json')
    console.log(posts)

    const urls = yield ajax('/api/urls11.json')
    console.log(urls)
  } catch (e) {
    console.log(e)
  }
}

function co (generator) {
  const g = generator()

  function handleResult (result) {
    if (result.done) return // The generator function ends
    result.value.then(data= > {
      handleResult(g.next(data))
    }, error= > {
      g.throw(error)
    })
  }

  handleResult(g.next())
}

co(main)

// const result = g.next()

// result.value.then(data => {
// const result2 = g.next(data)

// if (result2.done) return

// result2.value.then(data => {
// const result3 = g.next(data)

// if (result3.done) return

// result3.value.then(data => {
// g.next(data)
/ /})
/ /})
// })

Copy the code

12. Async scheme

// Async/Await syntax sugar

function ajax (url) {
  return new Promise((resolve, reject) = > {
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.responseType = 'json'
    xhr.onload = () = > {
      if (xhr.status === 200) {
        resolve(xhr.response)
      } else {
        reject(new Error(xhr.statusText))
      }
    }
    xhr.send()
  })
}

function co (generator) {
  const g = generator()

  function handleResult (result) {
    if (result.done) return // The generator function ends
    result.value.then(data= > {
      handleResult(g.next(data))
    }, error= > {
      g.throw(error)
    })
  }

  handleResult(g.next())
}

async function main () {
  try {
    const users = await ajax('/api/users.json')
    console.log(users)

    const posts = await ajax('/api/posts.json')
    console.log(posts)

    const urls = await ajax('/api/urls.json')
    console.log(urls)
  } catch (e) {
    console.log(e)
  }
}

// co(main)
const promise = main()

promise.then(() = > {
  console.log('all completed')})Copy the code

This article is mainly to understand Primise from the perspective of code, and the basic usage is written in the comments. If you have any questions, please leave a comment