同步、异步以及promise

75 阅读5分钟

进程与线程

  1. 进程(厂房)
    • 程序的运行环境
  2. 线程(工人)
    • 线程是实际进行运算的东西

同步与异步

同步的特点

  1. 通常情况代码都是自上向下一行一行执行的
  2. 前边的代码不执行后边的代码也不会执行
  3. 同步的代码执行会出现阻塞的情况
  4. 一行代码执行慢会影响整个程序的执行
function sum(a, b){
    let begin = Date.now()
    while(Date.now() - begin < 10000){

    }
    return a + b
}

console.log("第一行打印")
let result = sum(123, 456)
console.log(result)
console.log("第二行打印")

上面的代码sum()执行会停顿10秒,10秒后才会返回结果。由于是同步代码,所以sum()会阻塞其后所有代码的执行,导致整个程序的执行速度极差。

如何解决同步阻塞的问题

node.js通过异步来解决

异步的特点

  1. 不会阻塞其他代码的执行
  2. 需要通过回调函数来返回结果
  3. 异步的问题
    • 异步的代码无法通过return来设置返回值

基于回调函数的异步带来的问题

  1. 代码的可读性差
  2. 可调试性差

回调地狱

function sum (a, b, cb) {
  setTimeout(() => {
    cb(a + b)
  }, 1000)
}

console.log('111111')
sum(123, 456, (result) => {
  sum(result, 7, (result) => {
    sum(result, 8, result => {
      sum(result, 9, result => {
        sum(result, 10, result => {
          console.log(result)
        })
      })
    })
  })
})

console.log('222222')

假设需要调5次异步函数,后一个函数要依赖前一个的运算结果,这样就出现了回调地狱,也就是死亡金字塔,这样既不好维护,也增加了代码的复杂程度,变得难以理解。

解决问题

  • 需要一个东西,可以代替回调函数来给我们返回结果
  • Promise横空出世 - Promise是一个可以用来存储数据的对象,Promise存储数据的方式比较特殊,这种特殊方式使得Promise可以 用来存储异步调用的数据

Promise

  • Promise可以帮助我们解决异步中的回调函数的问题
  • Promise就是一个用来存储数据的容器 它拥有着一套特殊的存取数据的方式 这个方式使得它里面可以存储异步调用的结果

创建Promise

  • 创建Promise时,构造函数中需要一个函数作为参数
  • Promise构造函数的回调函数,他会在创建Promise时调用,调用时会有两个参数传递进去
const promise = new Promise((resolve,reject)=>{
    // resolve 和reject是两个函数,通过这两个函数可以向Promise中存储数据
    // resolve在执行正常时存储数据,reject在执行错误时存储数据
    // 通过函数来向Promise中添加数据,好处就是可以用来添加异步调用的数据
    resolve("哈哈")
})

从Promise中读取数据

  • 可以通过Promise的实例方法then来读取Promise中存储的数据
  • then需要两个回调函数作为参数,回调函数用来获取Promise中的数据
    • 通过resolve存储的数据,会调用第一个函数返回,可以在第一个函数中编写处理数据的代码
    • 通过reject存储的数据或者出现异常时,会调用第二个函数返回可以在第二个函数中编写处理异常的代码
promise.then((result) => {
  console.log('1', result)
}, (reason) => {
  console.log('2', reason)
}) 

Promise中维护的两个隐藏属性

  • PromiseResult
    • 用来存储数据
  • PromiseState
    • 记录Promise的状态(三种状态)
      • pending(进行中)
      • fulfilled(完成) 通过resolve存储数据时
      • rejected(拒绝,出错了) 出错了或者通过reject存储数据时
    • state只能修改一次,修改以后永远不能再变
  • 流程
    • 当Promise创建时,PromiseState初始值为pending
      • 当通过resolve存储数据时 PromiseState 变为fulfilled(完成) PromiseResult变为存储的数据
      • 当通过reject存储数据或出错时 PromiseState 变为rejected(拒绝,出错了) PromiseResult变为存储的数据 或 异常对象
    • 当我们通过then读取数据时,相当于为Promise设置了回调函数
      • 如果PromiseState变为fulfilled,则调用then的第一个回调函数来返回数据
      • 如果PromiseState变为rejected,则调用then的第二个回调函数来返回数据
    • catch()
      • 用法和then类似,但是只需要一个回调函数作为参数
      • catch()中的回调函数只会在Promise被拒绝时才会调用
      • catch()相当于then(null,reason=>{})
      • catch()就是一个专门处理Promise异常的方法
    • finally()
      • 无论是正常存储数据还是出现异常了,finally总会执行
      • 但是finally的回调函数中不会接收到数据
      • finally()通常用来编写一些无论成功与否都要执行的代码
const promise2 = new Promise((resolve, reject) => {
  // resolve('哈哈')
  reject('哈哈')
})

promise2.then(result => {
  console.log(result)
}, reason => {
  console.log('出错了')
})
promise2.catch(reason => {
  console.log(222222)
})
promise2.finally(() => {
  console.log('没有什么能够阻挡我执行的')
}) 

Promise的链式调用

对Promise进行链式调用时,后边的方法(then和catch)读取的上一步的执行结果,如果上一步的执行结果不是当前想要的结果,则跳过当前的方法。当Promise出现异常时,而整个调用链中没有出现catch,则异常会向外抛出。

promise
  .then(r => console.log("第一个then", r))
  .catch((r) => {
    throw new Error("报个错玩")
  })
  .then(r => console.log("第二个then", r))
  .catch(r => {
    console.log("出错了")
  })  //打印:出错了

Promise中的

  • then
  • catch
    • Promise中会存储回调函数的返回值
    • 这三个方法都会返回一个新的Promise
  • finally
    • finally的返回值,不会存储到新的Promise中
const promise = new Promise((resolve, reject) => {
  // reject('周一到周五19点,不见不散')
  resolve('哈哈')
})
promise
  .then(result => {
    console.log('回调函数', result)
    return '锄禾日当午'
  })
  .then(result => {
    console.log("第二个then", result)
    return "超哥真快乐"
  })
  .then(result => {
    console.log(result)
  })
 //打印:回调函数 哈哈
         第二个then 锄禾日当午
         超哥真快乐

用Promise改写之前的回调地狱

function sum(a,b){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve(a+b)
        },1000)
    })
}
sum(123,456)
    .then(result => result+7)
    .then(result => result+8)
    .then(result => result+9)

Promise的静态方法

  • Promise.resolve() 创建一个立即完成的Promise
Promise.resolve(10).then(r => {
  console.log(r)
}) 
  • Promise.reject() 创建一个立即拒绝的Promise
Promise.reject("错误")
  • Promise.all([...]) 同时返回多个Promise的执行结果,有一个报错,就返回错误
Promise.all([
  sum(123, 456),
  sum(5, 6),
  // Promise.reject('哈哈'),
  sum(33, 44)
]).then(r => {
  console.log(r)
})
  • Promise.allSettled([...]) 同时返回多个Promise的执行结果(无论成功或失败)
Promise.allSettled([
  sum(123, 456),
  sum(5, 6),
  Promise.reject('哈哈'),
  sum(33, 44)
]).then(r => {
  console.log(r)
}) 
  • Promise.race([...])返回执行最快的Promise(不考虑对错)
Promise.race([
  Promise.reject(1111),
  sum(123, 456),
  sum(5, 6),
  sum(33, 44)
]).then(r => {
  console.log(r)
}).catch(r => {
  console.log('错误')
}) 
  • Promise.any([...])返回执行最快的完成的Promise
Promise.any([
  Promise.reject(1111),
  Promise.reject(2222),
  Promise.reject(3333),
]).then(r => {
  console.log(r)
}).catch(r => {
  console.log('错误', r)
})