完整的学习并理解Premise

380 阅读7分钟

01 :了解异步

1:在js中事件,定时器,ajax,文件操作,数据库的操作,在js中耗时的操作,基本上都会设置成异步。

第一步:接下来我们以读取文件为例来了解promise的产生,首先新建一个dome

555.png

第二步在index.js写入读取文件的代码


import fs from 'fs'
fs.readFile('./text/a.txt','utf8',(err,data)=>{
    console.log(data);
})

fs.readFile('./text/b.txt','utf8',(err,data)=>{
    console.log(data);
})

fs.readFile('./text/c.txt','utf8',(err,data)=>{
    console.log(data);
})

fs.readFile('./text/d.txt','utf8',(err,data)=>{
    console.log(data);
})

在运行结果中发现异步的读取文件,是没有顺序的,每一次的运行结果都不一样,但是我想要的结果就是按顺序来读取a,b,c,d这四个文件,那应该怎么做呢。

1111.png

答案:其实很简单,因为异步的操作,我们的值是在外面拿不到,但是我们可以在里面等第一个异步读取a文件结束后,拿到结果了,我在进行读取b文件....这样就解决了。

import fs from 'fs'
fs.readFile('./text/a.txt','utf8',(err,data)=>{
    console.log(data);
    fs.readFile('./text/b.txt','utf8',(err,data)=>{
        console.log(data);

        fs.readFile('./text/c.txt','utf8',(err,data)=>{
            console.log(data);
            fs.readFile('./text/d.txt','utf8',(err,data)=>{
                console.log(data);
            })
        })
    })
})

222png.png

二:回调地狱的产生

  • 通过上面这种嵌套的方式,虽然实现了异步读取文件的操作,但是又产生了一个问题,通过层层嵌套的方式,产生的回调函数嵌套很多层,一旦项目复杂度高,这时候就很难维护代码,我们把这种层层嵌套的方式称为回调地狱。

三:Premise

Premise的出现,就是为了解决回调地狱,解决异步的处理,他是ES6中新增的,Promise可以理解为一个容器,里面可以编写的异步的代码。

语法 l

  • 一般用于处理成功后的数据:resolve
  • 一般用于处理失败后的失败 :rejects
  • .then方法接收两个函数类型的参数,分别用于接收resolve的值,rejects的值
  • .then方法也可以接收一个参数,表示只接收resolve的值,失败的结果可以通过链式调用的catch方法捕获
let p=new Promise ((resolve,rejects)=>{
    if(/*  */){
        resolve(value)
    }else{
        rejects(error)
    }
})

promise的三个状态

pending(默认) fulfilled(成功) rejected(失败)

  1. resolve函数被执行时, 会将promise的状态从 pending 改成 fulfilled 成功
  2. reject函数被执行时, 会将promise的状态从pending 改成 rejected 失败

并发的任务Promise.all

一、 Promise.all([promise1, promise2, promise3]) 等待原则, 是在所有promise都完成后执行, 可以用于处理一些并发的任务

场景:常用于loading效果的显示

后面的.then中配置的函数, 是在前面的所有promise都完成后执行, 可以用于处理一些并发的任务
Promise.all([promise1, promise2, promise3]).then((values) => {
 values 是一个数组, 会收集前面promise的结果 values[0] => promise1的成功的结果
})

image.png

二 、Promise.race([promise1, promise2, promise3]) 赛跑, 竞速原则, 只要三个promise中有一个满足条件, 就会执行.then(用的较少)

四 Premise实例用法

.then()可以接收两个参数,第一个函数处理成功,第二个函数失败

import fs from 'fs'
const p1= new Promise((resolve,reject)=>{
    // 在函数中,一定要有异步代码
    fs.readFile('./text/a.txt','utf8',(err,data)=>{
        console.log(data);
       if(err=null){
        // 没有错误,就把读取到的信息,通过resolve放到fs.readFile()外面去,
        resolve(data)
       }else{
        //  有错误,就把错误通过reject放到fs。readFile()外面去
        reject(err)
       }
    })
    
})

// p1里面就存储这resolve和reject抛出的值
// then()可以接收两个参数,第一个函数处理成功,第二个函数失败
p1.then(res=>{
    console.log(res);
},err=>{
console.log(err);
})

第二种方法.catch来读取

 第二种方法用.catch方法来捕获错误
 p1.then(res=>{
     console.log(res,"文件读取成功");
 }).catch(err=>{
     console.log(err,"文件读取失败");
 })

五:解决回调地狱

import fs from 'fs'
const p1= new Promise((resolve,reject)=>{
    // 在函数中,一定要有异步代码
    fs.readFile('./text/a.txt','utf8',(err,data)=>{
        resolve(data)
    })
    
})

const p2= new Promise((resolve,reject)=>{
    // 在函数中,一定要有异步代码
    fs.readFile('./text/b.txt','utf8',(err,data)=>{
        resolve(data)
    })
    
})
const p3= new Promise((resolve,reject)=>{
    // 在函数中,一定要有异步代码
    fs.readFile('./text/c.txt','utf8',(err,data)=>{
        resolve(data)
    })
    
})
const p4= new Promise((resolve,reject)=>{
    // 在函数中,一定要有异步代码
    fs.readFile('./text/d.txt','utf8',(err,data)=>{
        resolve(data)
    })
    
})

// p1里面就存储这resolve和reject抛出的值
// then()可以接收两个参数,第一个函数处理成功,第二个函数失败
p1.then(res=>{
    console.log(res);
})
p2.then(res=>{
    console.log(res);
})
p3.then(res=>{
    console.log(res);
})
p4.then(res=>{
    console.log(res)
})
console.log(p1,p2,p3,p4);

输出结果 2222.png 从输出结果来看虽然Promise用resolve把成功的结果返回到外面来了,通过.then的方法接收这Promise返回结果,但是从输出结果来看还是没有解决异步的问题

当然我也可以这样做,.then嵌套,但是这样回调地狱又产生了,始终没有解决,那Promise的意义也就看不出来了。

p1.then(res=>{
    console.log(res);
    p2.then(res=>{
        console.log(res);
        p3.then(res=>{
            console.log(res);
            p4.then(res=>{
                console.log(res);
            })
        })
    })
})

333.png

六:Promise完整写法

1: then()中的回调函数,不写返回值,:默认返回一个空数组Promise对象 如果返回一个真实的Promise对象,就会赋值给then()

**不return结果的时候,返回就是一个空Promise对象**

5555.png

import { log } from 'console'
import fs from 'fs'
const p1= new Promise((resolve,reject)=>{
    // 在函数中,一定要有异步代码
    fs.readFile('./text/a.txt','utf8',(err,data)=>{
        resolve(data)
    })
    
})

const p2= new Promise((resolve,reject)=>{
    // 在函数中,一定要有异步代码
    fs.readFile('./text/b.txt','utf8',(err,data)=>{
        resolve(data)
    })
    
})
const p3= new Promise((resolve,reject)=>{
    // 在函数中,一定要有异步代码
    fs.readFile('./text/c.txt','utf8',(err,data)=>{
        resolve(data)
    })
    
})
const p4= new Promise((resolve,reject)=>{
    // 在函数中,一定要有异步代码
    fs.readFile('./text/d.txt','utf8',(err,data)=>{
        resolve(data)
    })
    
})

// p1里面就存储这resolve和reject抛出的值
// then()可以接收两个参数,第一个函数处理成功,第二个函数失败 
const a= p1.then(res=>{
    console.log(res);
return p2
    
})

const b= a.then(res=>{
    console.log(res);
    return p3
})
 const c= b.then(res=>{
    console.log(res);
    return p4
})
c.then(res=>{
    console.log(res);
})
// then()中的回调函数,不写返回值,:默认返回一个空数组Promise对象 如果返回一个真实的Promise对象,就会赋值给then()

  1. 当然上面.then的方式用链式编程的写法来写就方便多了
  2. 返回一个Promise对象,调用下一个.then()
 p1.then(res=>{
    console.log(res);

    return p2
}).then(res=>{
    console.log(res);
   return p3
   
}).then(res=>{
    console.log(res);
   return p4
}).then(res=>{
    console.log(res);
})

七 封装Promise

import fs from 'fs'
//  promise(resolve,reject)的封装
function getPromise(url){
 return  new Promise((resolve,reject)=>{
    // 在函数中,一定要有异步代码
    fs.readFile(url,'utf8',(err,data)=>{
        resolve(data)
    }) 
})
}  
//调用
getPromise('./text/a.txt').then(res=>{
    console.log(res);
    return getPromise('./text/b.txt')
}).then(res=>{
    console.log(res);
    return getPromise('./text/c.txt')
}).then(res=>{
    console.log(res);
    return getPromise('./text/d.txt')
}).then(res=>{
    console.log(res);
})

八 第三方包then-fs 解决回调地狱。

// then.fs解决回调地狱
// 普通的fs模块,readFile()返回 undefined
// this-fs模块,readFile()返回,promise对象


import thenFs from 'then-fs';


thenFs.readFile('./text/a.txt','utf8').then(res=>{
    console.log(res);
    return thenFs.readFile('./text/b.txt','utf8')
}).then(res=>{
    console.log(res);
    return thenFs.readFile('./text/c.txt','utf-8')
}).then(res=>{
    console.log(res);
    return thenFs.readFile('./text/d.txt','utf8')
}).then(res=>{
    console.log(res);
})
  • 总结:Promise对象代表一个异步操作,有三种状态
  • Pending(进行中),此时的Promise的结果为undefined
  • Resolved(已完成,又称Fulfilled),此时Promise的结果为resolve函数的值
  • Rejected(已失败),此时Promise的结果为传递给reject的函数的值 需要注意的是:Promise被创建的时候,执行的是同步代码 then()和catch()里面执行的异步代码

八 async和await修饰符

import fs from 'then-fs'
 async function fn(){
     const fs1=await fs.readFile('./text/a.txt','utf8')
     console.log(fs1);
     const fs2=await fs.readFile('./text/b.txt','utf8')
     console.log(fs2);
     const fs3=await fs.readFile('./text/c.txt','utf8')
     console.log(fs3);
     const fs4=await fs.readFile('./text/d.txt','utf8')
     console.log(fs4);
 }
 fn()

ES7 标准中新增的 async 函数,从目前的内部实现来说其实就是 Generator 函数的语法糖。

它基于 Promise,并与所有现存的基于Promise 的 API 兼容。

async 关键字

  1. async 关键字用于声明⼀个异步函数(如 async function asyncTask1() {...}
  2. async 会⾃动将常规函数转换成 Promise,返回值也是⼀个 Promise 对象
  3. async 函数内部可以使⽤ await

await 关键字

  1. await 用于等待异步的功能执⾏完毕 var result = await someAsyncCall()
  2. await 放置在 Promise 调⽤之前,会强制async函数中其他代码等待,直到 Promise 完成并返回结果
  3. await 只能与 Promise ⼀起使⽤
  4. await 只能在 async 函数内部使⽤ 总结:await替代了then(),也不需要链式编程了

九. 相较于 Promise,async/await有何优势?

  1. 同步化代码的阅读体验(Promise 虽然摆脱了回调地狱,但 then 链式调⽤的阅读负担还是存在的)
  2. 和同步代码更一致的错误处理方式( async/await 可以⽤成熟的 try/catch 做处理,比 Promise 的错误捕获更简洁直观)
  3. 调试时的阅读性, 也相对更友好

try和catch捕获异常(扩展)

import fs from 'then-fs'
 async function fn(){
    try{
        const fs1=await fs.readFile('./text/a.txt','utf8')
        console.log(fs1);
        const fs2=await fs.readFile('./text/b.txt','utf8')
        console.log(fs2);
        const fs3=await fs.readFile('./text/c.txt','utf8')
        console.log(fs3);
        const fs4=await fs.readFile('./text/ddd.txt','utf8')
        console.log(fs4);
    }catch{
        console.log("文件读取错误");
    }finally{
        console.log("无论有没有错误都要执行的代码");
    }
 }
 fn()

输出结果

11.png