Promise

Promise

1、Promise是什么?

Promise 是异步编程的一种解决方案:从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。promise有三种状态: pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

相信大家经常会写这样的代码:

 // 当参数a大于10且参数fn2是一个方法时 执行fn2
 function fn1(a, fn2) {
     if (a > 10 && typeof fn2 == 'function') {
         fn2()
     }
 }
 fn1(11, function() {
     console.log('this is a callback')
 })

一般来说我们会碰到的回调嵌套都不会很多,一般就一到两级,但是某些情况下,回调嵌套很多时,代码就会非常繁琐,会给我们的编程带来很多的麻烦,这种情况俗称——回调地狱。

这时候我们的promise就应运而生了!

promise是用来解决两个问题的:

  • 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象

  • promise可以支持多个并发的请求,获取并发请求中的数据

  • 这个promise可以解决异步的问题,本身不能说promise是异步的

简单理解:

  • promise是js在进行异步编程的解决方案

  • promise支持链式调用,可以解决回调地狱问题!

  • promise用来封装一个异步操作并可以获取其成功/失败的结果值!

 ## 异步编程
 * fs 文件操作
 ```
 require('fs').readfile('./index.html',(err,data)=>{})
 ```
 * 数据库操作
 * AJAX
 ```
  $.get('./server',(data)=>{})
 ```
 * 定时器
 ```
 setTimeout(()=>{},2000)
 ```

 

2、Promise的API

  • .then() 获取promise的结果

  • .catch() 来获取失败时返出的错误信息

  • Promise.all( ) 接受多个promise的实例做为参数,参数必须是一个数组,promise都执行成功后,返回的是执行后的结果的一个数组,如果有一个失败了,返回的是第一个失败的返回值

  • Promise.race( ) 接受多个promise的实例做为参数,其中一个promise完成时执行,返回结果是第一个完成的promise的返回数据

 

 

3、es6 promise用法大全

Promise是一个构造函数,自己身上有all、reject、resolve这几个眼熟的方法,原型上有then、catch等同样很眼熟的方法。

简单写一个小案例:

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Document</title>
 </head>
 <body>
     <div id="container">
         <h2 class="page-handler">Promise 初体验 </h2>
         <button class="btn btn-primary" id="btn">点击抽奖</button>
     </div>
     <script>
         // 生成随机数
         function rand(m,n){
             return Math.ceil(Math.random() * ((n-m+1)) +m+1)
         }
 ​
         /*
             点击按钮 1s后显示是否中奖(中奖概率为30%)
                 若是中奖 弹出  恭喜您中奖了
                 若是没有中奖 显示再接再厉
         */ 
 ​
         //获取元素对象
         const btn = document.querySelector('#btn');
         // 绑定单击事件
         btn.addEventListener('click',()=>{
             // 定时器
             // setTimeout(() => {
             //     // 30% 1-100 
             //     // 获取1-100 的一个随机数
             //     let n = rand(1,100);
             //     if(n <= 30){
             //         alert('恭喜您中奖了')
             //     }else{
             //         alert('再接再厉')
             //     }
             // }, 100);
 ​
 ​
             // promise 形式解决!
             // resolve 解决  reject 拒绝 都是函数类型的数据!
             const p = new Promise((resolve,reject)=>{
                 setTimeout(() => {
                     // 30% 1-100 
                     // 获取1-100 的一个随机数
                     let n = rand(1,100);
                     if(n <= 30){ // 这里如果要加一个小需求,那就是将我们中奖或者失败的数字返回!
                         resolve(n) // 将promise的状态设置为 成功 !
                     }else{
                         reject(n) // 将promise的状态设置为 失败!
                     }
                 }, 100);
             })
 ​
             // 调用then 方法
             p.then((value)=>{
                 alert('中奖了'+value)
             },(reason)=>{
                 alert('再接再厉!'+reason)
             })
         })
 ​
 ​
     </script>
 </body>
 </html>

Promise的构造函数接收一个参数:函数,并且这个函数需要传入两个参数:

  • resolve :异步操作执行成功后的回调函数

  • reject:异步操作执行失败后的回调函数

Promise 状态:(Promise对象中的一个属性[PromiseState])

  • pending 未决定的

  • resolved / fullfilled 成功

  • rejected 失败

Promise 返回值:(PromiseResult)保存着异步任务成功或失败的结果!

  • resolve

  • reject

promise对文件的练习:

 const fs = require('fs')
 ​
 //回调函数的形式!
 // fs.readFile('./word.txt',(err,data)=>{
 //     // 如果出错,则抛出错误!
 //     if (err) throw err;
 //     // 输出文件内容!
 //     console.log(data.toString())
 ​
 // })
 ​
 // promise 形式
 const p = new Promise((resolve,reject) => {
     fs.readFile('./word.txt',(err,data)=>{
         if (err) reject()
         resolve(data)
     })
 })
 ​
 // 调用then
 p.then(value=>{
     console.log(value.toString());
 },reason=>{
     console.log(reason);
 })

 

then 链式操作的用法

所以,从表面上看,Promise只是能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。所以使用Promise的正确场景是这样的:

 p.then((data) => {
     console.log(data);
 })
 .then((data) => {
     console.log(data);
 })
 .then((data) => {
     console.log(data);
 });

reject的用法 :

把Promise的状态置为rejected,这样我们在then中就能捕捉到,然后执行“失败”情况的回调。

then中传了两个参数,then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。所以我们能够分别拿到他们传过来的数据。

 let p = new Promise((resolve, reject) => {
     //做一些异步操作
   setTimeout(function(){
         var num = Math.ceil(Math.random()*10); //生成1-10的随机数
         if(num<=5){
             resolve(num);
         }
         else{
             reject('数字太大了');
         }
   }, 2000);
 });
 p.then((data) => {
         console.log('resolved',data);
     },(err) => {
         console.log('rejected',err);
     }
 ); 

catch的用法

我们知道Promise对象除了then方法,还有一个catch方法,它是做什么用的呢?其实它和then的第二个参数一样,用来指定reject的回调。

 p.then((data) => {
     console.log('resolved',data);
 }).catch((err) => {
     console.log('rejected',err);
 });

效果和写在then的第二个参数里面一样。不过它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。请看下面的代码:

 p.then((data) => {
     console.log('resolved',data);
     console.log(somedata); //此处的somedata未定义
 })
 .catch((err) => {
     console.log('rejected',err);
 });

在resolve的回调中,我们console.log(somedata);而somedata这个变量是没有被定义的。如果我们不用Promise,代码运行到这里就直接在控制台报错了,不往下运行了。

也就是说进到catch方法里面去了,而且把错误原因传到了reason参数中。即便是有错误的代码也不会报错了,这与我们的try/catch语句有相同的功能

all的用法:

谁跑的慢,以谁为准执行回调。all接收一个数组参数,里面的值最终都算返回Promise对象

Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。看下面的例子:

 let Promise1 = new Promise(function(resolve, reject){})
 let Promise2 = new Promise(function(resolve, reject){})
 let Promise3 = new Promise(function(resolve, reject){})
 ​
 let p = Promise.all([Promise1, Promise2, Promise3])
 ​
 p.then(funciton(){
   // 三个都成功则成功  
 }, function(){
   // 只要有失败,则失败 
 })

有了all,你就可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据,是不是很酷?*有一个场景是很适合用这个的,一些游戏类的素材比较多的应用,打开网页时,预先加载需要用到的各种资源如图片、flash以及各种静态文件。所有的都加载完后,我们再进行页面的初始化。

race的用法:谁跑的快,以谁为准执行回调

race的使用场景:比如我们可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作,代码如下:

  //请求某个图片资源
     function requestImg(){
         var p = new Promise((resolve, reject) => {
             var img = new Image();
             img.onload = function(){
                 resolve(img);
             }
             img.src = '图片的路径';
         });
         return p;
     }
     //延时函数,用于给请求计时
     function timeout(){
         var p = new Promise((resolve, reject) => {
             setTimeout(() => {
                 reject('图片请求超时');
             }, 5000);
         });
         return p;
     }
     Promise.race([requestImg(), timeout()]).then((data) =>{
         console.log(data);
     }).catch((err) => {
         console.log(err);
     });

requestImg函数会异步请求一张图片,我把地址写为"图片的路径",所以肯定是无法成功请求到的。timeout函数是一个延时5秒的异步操作。我们把这两个返回Promise对象的函数放进race,于是他俩就会赛跑,如果5秒之内图片请求成功了,那么遍进入then方法,执行正常的流程。如果5秒钟图片还未成功返回,那么timeout就跑赢了,则进入catch,报出“图片请求超时”的信息。

 

4、async/await的区别

1、什么是async/await

说明:同样是解决异步操作,promise是ES6语法,async和await是ES7语法

1、返回值是promise对象。

2、在使用异步的操作前+await,函数写async关键字。

3、await只有再含async的函数中才生效。

4、想让其为异步操作,返回值变为promise。

5、出现了await关键之之后的代码都要在其执行之后才会执行(下图的wwww要等await执行之后才会执行)

async/await是写异步代码的新方式,它是generator的语法糖,以前的方法有回调函数和 Promise。async/await是基于Promise实现的,它不能用于普通的回调函数。 async/await与Promise一样,是非阻塞的。async/await使得异步代码看起来像同步代码,这正是它的魔力所在。

2、什么是Promise

  • Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。所谓promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。

async/await语法

  • 使用Promise

 Promise.resolve().then(data=>{//promise
     console.log(data)
     return "aaa"
  })
  • 使用Async

 async function foo() {
   console.log(await getJSON)
     return "aaa"
 }
 foo();

区别

  1. 函数前面多了一个async关键字。await关键字只能用于async定于的函数内。async函数会隐式地返回一个Promise,该promise的resolve值就是return的值。示例中resolve的值就是字符串"aaa"

  2. await的必须在异步函数中使用;其中await有限制(不允许出现在箭头函数中;不允许出现在同步函数声明中;不允许出现在同步函数表达式中;如果在同步函数中使用await就会抛出SyntaxError。)

 //不允许出现在箭头函数中
 function foo(){
   const syncFn = () => {
     return await Promise.resolve('foo');
     };
     console.log(syncFn());
 }
 //不允许出现在同步函数声明中
 function bar(){
   const syncFn=function ()  {
     return await Promise.resolve('bar');
     };
     console.log(syncFn());
 }
 //不允许出现在同步函数表达式中
 function baz(){
   const syncFn=function ()  {
     return await Promise.resolve('baz');
     };
     console.log(syncFn());
 }

为什么async/await更好?

  1. 使用async函数可以让代码简洁很多,不需要想Promise一样需要then,不需要写匿名函数处理Promise的resolve的值,也不需要定义多余的data变量,还避免了嵌套代码。

  2. async/await 让 try/catch可以同时处理同步和异步的错误。在下面的示例中,try/catch不能处理JSON.parse的错误,因为它在Promise中,我们需要使用.catch,这样的错误会显得代码非常的冗余。

 const makeRequest = () => {
   try {
     getJSON().then(result => {
     // JSON.parse可能会出错
     const data = JSON.parse(result)
     console.log(data)
      })
    // 取消注释,处理异步代码的错误
    // .catch((err) => {
    //   console.log(err)
    // })
    } catch (err) {
     console.log(err)
         }
     }

使用async/await的话,catch能处理JSON.parse错误

 const makeRequest = async () => {
   try {
     // this parse may fail
     const data = JSON.parse(await getJSON())
     console.log(data)
   } catch (err) {
     console.log(err)
   }
 }

 

 

 

 

 

 

 

 

 

posted @   nakano_may  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
点击右上角即可分享
微信分享提示