小白学promise

85 阅读6分钟

一.什么是Promise:

  1. Promise 是在 js 中进行异步编程的新解决方案。(以前旧的方案是单纯使用回调函数)
  2. 从语法来说,promise是一个构造函数。
  3. 从功能来说,promise对象用来封装一个异步操作,并且可以获得成功或失败的返回值。
  4. JS中的常见的异步操作:定时器,AJAX中一般也是异步操作(也可以同步),回调函数可以理解为异步(不是严谨的异步操作)…等。
    剩下的都是同步处理

二.为啥使用Promise:

  1. promise使用回调函数更灵活。旧的回调函数必须在启动异步任务前指定。
  2. promise:启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数(甚至能在异步任务结束后指定多个)
  3. promise支持链式调用,可以解决回调地狱问题。(回调地狱就是多层回调函数嵌套使用,就是套娃,这样就不利于阅读和异常处理。)

三:promise体验网络请求:

效果:点击按钮获得一句名言在控制台输出。\

一、不用promise的原始写法:

 <script>
        var btn = document.querySelector("#btn");
    // 点击事件
      btn.addEventListener('click',function(){
         //创建对象
         const xhr = new XMLHttpRequest();
         //初始化
         xhr.open('GET',"http://poetry.apiopen.top/sentences");
         //发送
         xhr.send();
         //处理响应结果
         xhr.onreadystatechange = function(){
             if(xhr.readyState === 4){
                 if(xhr.status >=200 && xhr.status < 300){
                     //输出响应体
                     console.log(xhr.response);
                 }else{
                     //输出响应状态码
                     console.log(xhr.status);
                 }
             }
         }
     })
    </script>

执行后的结果: image.png

二、promise的写法

 // 点击事件
     btn.addEventListener('click',function(){
         const p = new Promise((resolve,reject) => {
              //创建对象
         const xhr = new XMLHttpRequest();
             //初始化
             xhr.open('GET', "http://poetry.apiopen.top/sentences");
             //发送
             xhr.send();
             //处理响应结果
             xhr.onreadystatechange = function () {
                 if (xhr.readyState === 4) {
                     if (xhr.status >= 200 && xhr.status < 300) {
                         //响应成功,通过resolve输出响应体
                        resolve(xhr.response);
                     } else {
                         //请求失败,通过reject输出响应状态码
                         reject(xhr.status);
                     }
                 }
             }
         })
         p.then(value=>{
              console.log(value);
         },reason=>{
             //控制台输出警告信息
               console.warn(reason);
         })
     })

效果一样:

image.png

四:promise的状态改变:

promise状态表示实例对象的一个属性【PromiseState】。包括以下值:
(1)pending 未决定的
(2)resolved 或 fullfilled 成功
(3)rejected 失败
Promise对象的值表示实例对象的另一个属性【PromiseResult】。保存着对象【成功/失败】的结果。而其状态改变只有以下两种可能:
(1)pending 变为resolved
(2)pending 变为 rejected
注:一个promise对象只能改变一次,无论成功或失败都会有一个结果数据,成功的称为 value , 失败的称为 reason 。

五、Promise基本流程图:

Promise基本流程图

六、Promise的 使用:

1. Promise 的构造函数:Promise(executor){}

(1)executor 函数:执行器  (resolve,reject)=> {}
(2)resolve 函数:内部定义成功时调用函数 value => {} 。
(3)reject 函数:内部定义失败时调用函数 reason => {} 。
注意:Promise内部会立同步即调用executor,异步操作在执行器里执行。

2. Promise.prototype.then 方法: (onResolved, onRejected)=> {}

(1) onResolved 函数:成功的回调函数  (value) => {}
(2) onRejected 函数:失败的回调函数  (reason) => {}
注:指定用于得到成功value的成功回调和用于得到失败reason的失败回调是返回一个新的promise对象。

3.Promise.prototype.catch万法: (onRejected) => {}

onRejected.函数: 失败的回调函数**(reason)=> {}**
注:只是失败的调用。then()的语法糖,相当于: then(undefined, onRejected)。

4. Promise.resolve 方法: (value)=> {}

value: 成功的数据或promise对象
注:如果传入的参数为非Promise类 型的对象,则返回的结果为成功promise对象,如果传入的参数为Promise 对象,则参数的结果决定了resolve 的结果。

5. Promise.reject 方法: (reason) => {}

reason: 失败的原因
注:无论传入啥只返回一个失败的promise对象。

6. Promise.all 方法: (promises)=> {}

promises: 包含n个promise的数组
注:返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败了就直接失败。失败了返回那个失败值。

7. Promise.race 方法: (promises)=> {}

promises: 包含n个promise的数组
注:返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态。

七:async函数:

MDN文档

1.函数的返回值为promise对象。
2.promise对象的结果由async函数执行的返回值决定。
3.其实跟 then()方法返回结果是一样一样的。

1.返回一个非Promise对象,返回值是resolve。

 async function main(){
             return '123';
          }
          let res = main();
          console.log(res);

在这里插入图片描述

2.如果返回是一个Promise对象,由返回结果决定:

如:

 async function main(){
             return new Promise((resolve,reject)=>{
                 reject('NO');
             });
          }
          let res = main();
          console.log(res);

在这里插入图片描述

3.抛出异常也是失败:

 async function main(){
             return new Promise((resolve,reject)=>{
                 reject('NO');
             });
          }
          let res = main();
          console.log(res);

在这里插入图片描述

八.await表达式:

MDN文档

1.await右侧的表达式一般为promise对象,但也可以是其它的值。
2.如果表达式是promise对象,await返回的是promise成功的值。
3.如果表达式是其它值,直接将此值作为await的返回值。

注意

1.await 必须写在async函数中,但async 函数中可以没有await 。
2.如果await的promise失败了,就会抛出异常,需要通过try…catch捕获处理。

1.右侧为promise对象:

 async function works(){
             let p = new Promise((resolve,reject)=>{
                 resolve('oh yes')
             })
             let res = await p;
             console.log(res);
         }
         works();

12345678

结果:
oh yes

2.右侧为其它值:

  async function works(){
             let p = new Promise((resolve,reject)=>{
                 resolve('oh yes')
             })
           //  let res = await p;
           let res = await 100;
             console.log(res);
         }
         works();

结果:
100

3.如果promise是失败状态:

 async function works(){
             let p = new Promise((resolve,reject)=>{
                // resolve('oh yes')
                reject('err');
             })          
            try{
                let res = await p;
            }catch(e){
                console.log(e);
            }
         }
         works();

在这里插入图片描述

九.async与await的应用场景

async/await 语法可以把异步请求代码写成同步代码的形式,有如下日常常用场景

场景1. 一个请求接着一个请求

后一个请求依赖前一个请求,下面以爬取一个网页内的图片为例子进行描述,使用了 superagent 请求模块, cheerio 页面分析模块,图片的地址需要分析网页内容得出,所以必须按顺序进行请求。


const request = require('superagent')
const cheerio = require('cheerio')
// 简单封装下请求,其他的类似

function getHTML(url) {
// 一些操作,比如设置一下请求头信息
return superagent.get(url).set('referer', referer).set('user-agent', userAgent)
}
// 下面就请求一张图片
async function imageCrawler(url) {
    let res = await getHTML(url)
    let html = res.text
    let $ = cheerio.load(html)
    let $img = $(selector)[0]
    let href = $img.attribs.src
    res = await getImage(href)
    retrun res.body
}
async function handler(url) {
    let img = await imageCrawler(url)
    console.log(img) // buffer 格式的数据
    // 处理图片
}
handler(url)

注意 await getHTML 是必须的,如果省略了 await 程序就不能按预期得到结果。

当遇到await关键字时,它会暂停该函数的进一步执行,直到正在等待的promise被解决 才会在该函数去向下执行,但是它不会堵塞其他函数的执行(不是阻塞同步代码所在的主线程)

虽然await阻塞异步函数向后执行,看起来像是同步的,但是它本质还是异步的,我们同样可以并行执行其他的不关联的异步操作,而同步函数不能并行执行。

场景2.并发请求

获取到一张图片,但是可能一个网页中不止一张图片,如果我们要把这些图片存储起来,其实是没有必要等待图片都并发请求回来后再处理,哪张图片早回来就存储哪张就行了

let imageUrls = ['href1', 'href2', 'href3']
async function saveImages(imageUrls) {
    await Promise.all(imageUrls.map(async imageUrl => {
    let img = await getImage(imageUrl)
    return await saveImage(img)
}))
    console.log('done')
}
// 如果我们连存储是否全部完成也不关心,也可以这么写
let imageUrls = ['href1', 'href2', 'href3']
// saveImages() 连 async 都省了
function saveImages(imageUrls) {
    imageUrls.forEach(async imageUrl => {
    let img = await getImage(imageUrl)
    saveImage(img)
    })
}
复制代码