Promise

简介: 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)
  }
}
相关文章
|
前端开发 小程序 JavaScript
promise 应用
promise 应用
59 0
|
前端开发 JavaScript 测试技术
6 # 实现简单的 promise
6 # 实现简单的 promise
40 0
|
5月前
|
前端开发
|
6月前
|
前端开发 JavaScript
Promise 详解
Promise 详解
63 0
|
11月前
|
前端开发
Promise
Promise
57 1
|
存储 JSON 前端开发
深入使用 Promise
前面各种铺垫已经好了,现在我们一起来用一用Promise!
75 0
|
前端开发
对promise的理解分享
对promise的理解分享
|
前端开发
什么是promise,解决了什么
什么是promise,解决了什么
154 0
|
前端开发 JavaScript
深入理解Promise
深入理解Promise
89 0
|
前端开发 JavaScript
Promise使用详解
JS查漏补缺系列是我在学习JS高级语法时做的笔记,通过实践费曼学习法进一步加深自己对其的理解,也希望别人能通过我的笔记能学习到相关的知识点。这一次我们来了解Promise使用详解
102 0