异步编程&Promise

82 阅读4分钟

进程和线程

进程

  • 程序的运行环境(相当于生产的工厂)

线程

  • 线程是实际进行运算的东西(相当于工厂里的工人)

同步

function sum(a, b){
    return a + b
}

console.log("第一行打印")
let result = sum(123, 456)
console.log(result)
console.log("第二行打印")
  • 代码自上而下一步一步运行
  • 前边代码不执行后边的代码也不会执行
  • 同步的代码执行会出现阻塞的情况
  • 一行代码慢会影响到整个程序的执行

解决同步问题:

  • Java、Python通过多线程来解决
  • node.js通过异步方式来解决

异步

  • 一段代码的执行不会影响其他程序的执行
  • 异步代码无法通过return来返回值(有时间差)——>解决这个问题所以用回调函数 基于“回调函数”的异步带来的问题
  • 使用回调函数时会导致“回调地狱”的情况
  • 代码可读性差
  • 可调式性差
function sum(a, b){
    setTimeout(()=>{
        return a + b
    }, 10000)
}
console.log("第一行打印")
let result = sum(123, 456)
console.log(result) //undefined
console.log("第二行打印")
function sum(a, b, cb){
    setTimeout(()=>{
        cb(a + b)
    }, 10000)
}

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

回调地狱(如下:)代码可读性低且不好调试【这个问题可以用Promise来解决】

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

sum(123, 456, result => {
    sum(result, 777, result => {
        sum(result, 888, result => {
            sum(result, 999, result => {
                console.log(result)
            })
        })
    })
})

Promise

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

创建Promise

创建Promise时,构造函数中需要一个函数作为参数
Promise构造函数的回调函数,它会在创建Promise时调用,调用时会有两个参数传递进去

const promise = new Promise(executor)
const promise = new Promise((resolve, reject) => {
    //resolve和reject是两个函数,通过这两个函数可以向Promise中存储数据
    //resolve在执行正常时存储数据,reject在执行错误时存储数据
    //通过函数来向Promise中添加数据,好处就是可以用来添加异步调用的数据
    setTimeout(()=>{
        resolve('哈哈')
    },2000) 
})

读取Promise数据

  • 可以通过Promise的实例方法then来读取Promise中存储的数据

  • then需要两个回调函数作为参数,回调函数用来获取Promise中的数据
    通过resolve存储的数据,会调用第一个函数返回()
    可以在第一个函数中编写处理数据的代码

    通过reject存储的数据,会调用第二个函数返回
    可以在第一个函数中编写处理异常的代码

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("哈哈")
    }, 10000)
})

promise.then((data) => {
    console.log(data) // "哈哈"
})
  • Promise中维护了两个隐藏属性:
    • PromiseResult
      • 用来存储数据
    • PromiseState
      • 记录Promise的状态(三种状态)
      • state只能修改一次,修改以后永远不会再变
        • pending
        • fulfilled
        • rejected

catch

  • catch()用法和then类似,但是只需要一个回调函数作为参数
  • catch()中的回调函数只会在promise被拒绝时才调用

Promise静态方法

  • Promise.resolve() 创建一个立即完成的Promise
  • Promise.reject()创建一个立即拒绝的Promise
  • Promise.all([...]) 里面有一个错误所有错误都不返回
  • Promise.allSettled([...])同时返回多个数据的结果(无论成功或失败)
  • Promise.race() 会返回首先执行完的Promise,而忽略其他未执行完的Promise
  • Promise.any() any会race类似,但是它只会返回第一个成功的Promise,如果所有的Promise都失败才会返回一个错误信息。

asyns和await.js

nodejs文档地址:nodejs.dev/en

  • 通过async可以快速创建异步函数
    • 异步函数的返回值会自动封装到一个Promise中返回
  • 在async声明的异步函数中可以使用await关键字来调用异步函数
async function fn2(){
    return 10
}
//此时fn2就是一个异步函数

//想要读到10要用then方法
fn2().then(r=>{
    console.log(r)
})

await用来解决链式调用太多的问题

function sum(a,b) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(a+b)
        }, 2000);
    })
}

/*
async function fn3() {
    sum(123,456)
        .then(r => sum(r,8)
        .then(r => sum(r,9)
        .then(r => console.log(r)
}
*/

let result = await sum(123,456) 
// await表示等待,当我们通过await去调用异步函数时,它会暂停代码的运行;直到异步代码执行有结果时,才会将结果返回
// 注意:await只能用于 async声明的异步函数中,或es模块的顶级作用域中
// await阻塞的是异步函数内部的代码,不会影响外部代码
// 通过await调用异步代码时,需要通过try-catch处理异常
// 如果async声明的异步函数中没有写await,那么它里边的代码就会依次执行


async function fn4() {
    console.log(1)
    /*
    当我们使用await调用函数后,当前函数后边的所有代码
        会在当前函数执行完毕后,被放入到微任务队伍里(范围在async里)
    */
    await console.log(2)
    console.log(3)
}

fn4()
console.log(4)

//结果:1 2 4 3