乐趣区

关于前端:Promise这样理解更简单

一、Promise 小白怎么用?从一个故事开始吧
1、先来一段废话故事
您是一名在古老迷失城市中探险的冒险家。您身处一间装璜富丽的房间中,周围布满了古老的壁画和雕塑。您发现有两个通道别离通向不同的方向,别离是:一个光明的通道和一个亮堂的通道。

光明的通道中充斥了神秘的气味,您能感触到其中蕴含的古老力量。您并不知道它会带您去哪里,但您能够感触到其中蕴藏的危险和未知。另一方面,亮堂的通道看起来和煦舒服,您能够看到远处照耀的阳光。您能听到一些欢快的音乐声,仿佛在那个方向上有一些人在欢庆。

当初,您须要在这两个通道之间做出抉择。您会抉择哪一个通道呢?您的抉择将会影响您接下来的冒险之旅。

2、再来看一段神秘的小短片
https://drive.weixin.qq.com/s?k=AOoAUwfiAAwVgJbsSaAUEA_QbjAFk

3、这么久了还不进入正题
先来理解几个单词,几个 Promise 的术语

● Promise 的三个状态:pending(期待状态)、fulfilled(已胜利状态)和 rejected(已失败状态)

● Promise 的三个罕用回调函数(咱说的是罕用):

  1. 实例化对象时传入的回调函数
  2. then 办法的传入的两个回调函数
  3. catch 办法传入的一个回调函数

以作业为例,持续深刻理解 Promise

1、开始写作业,此时 Promise 状态为 pending,咱们能够了解为初始状态,也能够了解为业务解决中

2、作业实现状况有两种,一种是写完了,一种是被狗吃了

3、无论哪种状况,必须要通知老师,胜利了就通过 resolve 通道暂存数据,同时会更改状态为胜利 fulfilled;失败了就通过 reject 通道暂存数据,同时批改状态为 rejected。

4、老师如何获取 resolve 或 reject 通道传来的数据呢?能够应用 Promise 对象的.then 办法获取 resolve 或 reject 的后果,或者应用.catch 办法获取 reject 通道的信息。

5、简略来一段代码看

const p = new Promise(function (resolve, reject) {resolve('通过胜利通道存储数据')
})

//... 如果不应用 then,提取,则数据始终暂存在 p 对象中

// 提取后果
p.then(function (val) {console.log(val)
})

6、这些写法都是固定的,倡议应用 promise 之前肯定要充沛理解回调函数

7、咱们再降级一下,写作业用了 5 秒钟

const p1 = new Promise(function (resolve, reject) {
  // 写作业用了 5 秒钟,5 秒钟之后,把胜利的作业暂存到 resolve 通道中
  setTimeout(() => {resolve('这是作业')
  }, 5000)
})
// 下面代码中调用了 resolve 的时候,then 外面的回调函数才会触发,这里有个时间差或者时空差的感觉
p1.then(function (val) {console.log(val)
})

8、再降级一下,这作业他也有被狗吃了的时候,咱们假设 5 秒钟之外就被狗吃了,5 秒钟之内就是实现的

const p2 = new Promise(function (resolve, reject) {
  // 生成一个 1~6 秒之间的随机数
  const time = Math.random() * 4000 + 2000
  setTimeout(() => {if (time <= 5000) {resolve('胜利交作业啦')
    } else {reject(` 作业被狗吃了,消耗了 ${time 秒}`)
    }
  }, time)
})
// 胜利应用 then 来接,失败了怎么拿到参数呢?// 用 then 的第二个参数来拿失败的参数
p2.then(function (val) {console.log(val)
}, function (err) {console.log('预计是被狗吃了', err)
})

9、除了 then 的第二个参数能够拿到失败的后果,还能够通过 catch 办法拿到后果,一会再探讨这两种用法的区别,先看 catch 的用法,留神这里须要连用

p2.then(function (val) {console.log(val)
}).catch((reason) => {
  // 输入失败起因,大概率是被狗吃了
  console.log('预计是被狗吃了', reason)
})

10、再看一种罕用的连用的写法

new Promise(function (resolve, reject) {
  // 生成一个 1~6 秒之间的随机数
  const time = Math.random() * 4000 + 2000
  setTimeout(() => {if (time <= 5000) {resolve('胜利交作业啦')
    } else {reject(` 作业被狗吃了,消耗了 ${time}秒 `)
    }
  }, time)
}).then(function (val) {console.log(val)
}).catch((reason) => {
  // 输入失败起因,大概率是被狗吃了
  console.log('预计是被狗吃了', reason)
})

11、一些需注意的中央

  1. resolve 和 reject 只是一个形参的名字,对应理论的值是 promise 外部的函数,调用这两个其实调用的就是 promise 外部的某个函数而已,这个名字能够轻易去改,例如
new Promise(function (ok, fail) {
  // 此时此刻形参叫 ok,但理论代表的是 promise 外部函数
  ok('ojbk')
}).then((res) => {
  //promise 外部会存储数据,传给 res 这个变量,此时 res 值就是 ojbk
  console.log(res)
})
    2.  new Promise(结构器的参数是一个函数),这个函数会同步执行,代码执行到这里的时候就会立刻执行

12、小结

  1. Promise 通过构造函数同步执行,执行后果调用 promise 的外部函数存储,通常叫 resolve 和 reject,一个是胜利时存储存储应用的通道,另一个是失败时存储的通道,无论存储到哪个通道中,都是写代码的人去定义的
  2. then 有两个参数,别离是两个函数类型的参数,第一个参数是在调用 resolve 时会触发,第二个参数是在调用 reject 时触发
  3. catch 办法能够代替 then 的第二个参数,拿到 reject 后果

13、附赠 then 第二个参数和 catch 的区别

在 then 的第一个参数外面的代码,如果出现异常的时候,不必手动的 try…catch,通过 promise 实例对象的.catch 能够捕捉 then 内呈现的异样,但留神,catch 不会捕捉构造函数代码中的谬误,来看例子

new Promise(function (ok, fail) {setTimeout(() => {
    // 成心 5 秒后触发 k 的报错
    console.log(k)
  }, 5000)
}).then((res) => {console.log(res)
}).catch(error => {// 这个时候,error 是拿不到那个谬误的,他不负责 console.log(k)所在代码块中呈现的谬误
  console.log(error)
})

再看一个 catch 办法能捕捉什么中央的谬误

大略就是这么个大略

二、为什么要这么用,图个啥~
应用 Promise 的次要起因是他能够解决回调天堂 (回调嵌套) 问题,让代码变得更优雅,逻辑更清晰

举一个生存中的例子,早上起床第一件事是要穿拖鞋,第二件事是洗漱,第三件事是穿衣服,第四件事是查看“本领要钱”,第五件事是关上自家房门进来开车下班,每件事都须要串行,每一步与下一步都有关联关系

function foo() {
  //1、穿拖鞋开始
  setTimeout(() => {
    //1、2 秒后穿拖鞋实现
    //2、洗漱开始
    setTimeout(() => {
      //2、2 秒后洗漱实现
      //3、穿衣服开始
      setTimeout(()=>{
        //3、穿衣服实现
        //.... 不好意思看官,后边还有好几个步骤咱就意思一下,再写就吐了
      },2000)
    }, 2000)
  }, 2000)
}
foo()

就写这几层吧,是不是太恶心了,上面咱们应用 Promise 来革新一下

new Promise((resolve, reject) => {
  //1、穿拖鞋
  setTimeout(() => {resolve('穿拖鞋搞定')
  }, 2000)
}).then(val => {
  // 期待穿拖鞋实现后,会调用这个函数
  //2、洗漱
  // 留神此处!!!,必须应用 return 返回一个新的 promise 来实现链式调用
  const p = new Promise((resolve, reject) => {setTimeout(() => {resolve('洗漱搞定')
    }, 2000)
  })
  return p
}).then(val => {
  //3、穿衣服,此处间接返回,没有应用两头变量
  return new Promise((resolve, reject) => {setTimeout(() => {resolve('穿衣服搞定')
    }, 2000)
  })
}).then(val => {
  //4、查看“本领要钱”return new Promise((resolve, reject) => {setTimeout(() => {resolve('查看“本领要钱”搞定')
    }, 2000)
  })
}).then(val => {
  //5、开车去下班
  // 元气满满的一天
  console.log(val+'元气满满的一天')
})

就图这~

三、Promise 其余办法
那么多办法,不讲那么多,race、all 什么的网上一抓一大把

说说语法糖 await 和 async 的用法
先理解一个根底规定

● await 必须润饰的是 Promise 对象

● await 必须在 async 中应用

● await 只能接管 resolve 通道的后果,reject 后果会导致报错

● await 润饰的代码在执行时,代码是卡住的,相似于 alert,这句代码不执行完,后边的代码不会向下执行,这也相似于线程同步执行的概念,这也是 await 有用之处

● async 润饰的必须是函数

● async 润饰后的函数在执行之后会转为 Promise 对象

看一段简略的代码

async function foo() {let k = await new Promise(function (resolve, reject) {setTimeout(() => {resolve('qfedu')
    }, 2000)
  })
  console.log(k)
}

foo()

这样用倒是更麻烦,咱们把要解决的事黏黏糊糊多弄一些试一试

async function foo() {let level1 = await new Promise((resolve, reject) => {
    //1、穿拖鞋
    setTimeout(() => {resolve('穿拖鞋搞定')
    }, 1000)
  })
  // 拿着第一步的后果,去第二步进行操作

  let level2 = await new Promise((resolve, reject) => {setTimeout(() => {resolve(level1 + '洗漱搞定')
    }, 1000)
  })

  let level3 = await new Promise((resolve, reject) => {setTimeout(() => {resolve(level2 + '穿衣服搞定')
    }, 1000)
  })

  let level4 = await new Promise((resolve, reject) => {setTimeout(() => {resolve(level3 + '查看“本领要钱”搞定')
    }, 1000)
  })

  console.log(level4 + ',之后开车去下班,元气满满的一天')
}

foo()

输入后果:

这样代码看起来更加简洁,当然要重点思考的问题是在整个从上到下的调用过程中,任何一个环节呈现问题,都会影响上面的代码

再来,咱们把代码聚焦到 foo()办法调用之前和调用之后

console.log(1)
foo()  // 这个会输入 穿拖鞋搞定洗漱搞定穿衣服搞定查看“本领要钱”搞定...... 等
console.log(2)

思考一下,程序输入的程序留神,应用 async 包裹的代码,属于异步代码,会在同步代码之后执行

咱们给按钮增加一个点击事件,看点击按钮如何让程序应用 await 程序执行

async function foo() {let level1 = await new Promise((resolve, reject) => {
    //1、穿拖鞋
    setTimeout(() => {resolve('穿拖鞋搞定')
    }, 1000)
  })
  // 拿着第一步的后果,去第二步进行操作

  let level2 = await new Promise((resolve, reject) => {setTimeout(() => {resolve(level1 + '洗漱搞定')
    }, 1000)
  })

  let level3 = await new Promise((resolve, reject) => {setTimeout(() => {resolve(level2 + '穿衣服搞定')
    }, 1000)
  })

  let level4 = await new Promise((resolve, reject) => {setTimeout(() => {resolve(level3 + '查看“本领要钱”搞定')
    }, 1000)
  })

  console.log(level4 + ',之后开车去 qfedu 下班,元气满满的一天')
}

window.onclick = foo;
// 或者是
window.onclick = async function(){
  //todo ...
  //await new Promise...
  //await new Promise...
  //await new Promise...
  //...
}

理论场景中,await 与 async 通常用来解决 ajax 申请类代码,让代码更简洁,再次强调,await 只接管 resolve 后果,留神 reject 和 error 的谬误要应用 try…catch… 解决异样

退出移动版