美文网首页js
Promise和async/await(一)

Promise和async/await(一)

作者: 书虫和泰迪熊 | 来源:发表于2020-07-08 11:49 被阅读0次

    为什么会有同步和异步?

    首先JS 是单线程的,单线程程序在执行的时候,所有的程序都是按照顺序执行的,前面的必须处理好后面的才会执行。
    JS 是单线程的,但是浏览器加载一些需要网络请求的资源,ajax或者执行一段 setTimeout代码,由于是单线程,要等这些内容访问或者执行完才执行下面的代码,那么你发送ajax请求,执行setTimeout 的这段时间什么也做不了,这种效果对程序是一种堵塞(同步堵塞),这个时候异步就出现了,在涉及需要等待的操作,我们把代码交给其他对应的浏览器线程去执行,在执行结束的时候,通知我们的主线程执行完毕,你可以操作资源了,这段等待时间并不影响你程序的执行,只是在未来的某个时间段(不确定),有一个操作一定执行,这就是异步(异步非阻塞)

    回调

    异步和同步相比,最难掌控的就是异步任务会什么时候完成和完成之后的回调问题。
    回调是异步编程最基本的方法
    像下面的例子

    listen( "click", function handler(evt){
        setTimeout( function request(){
            ajax( "http://some.url.1", function response(text){
                if (text == "hello") {
                    handler();
                }
                else if (text == "world") {
                    request();
                }
            } );
        }, 500) ;
    } );
    console.log('doSomething')
    

    这种地域式的回调,令代码的可读性非常差!!

    信任问题

    在你不知道的javascript一书中,对于回调的信任问题做了阐述 当你使用第三方的库的方法处理回调时很有可能遇到以下信任内容
    · 调用回调的过早
    · 调用回调过晚
    · 调用回调次数过多会过少
    · 没有把所需要的环境/参数成功的传给你的回调函数
    · 吞掉可能出现的错误或异常
    · .......
    那么怎么解决这种信任问题呢?

    你需要一个承诺

    当你把一件事情交给别人去做的时候,这个任务可能马上完成也可能一段时间后完成,这个人在任务完成或者失败的时候回给你一个回应,放心的人!!回应就表示成功了或者失败了,没回应就表示正在执行~~~

    Promise(承诺) 就是这样人

    【官方】Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象
    三种状态:
    1.pending(进行中)
    2.fulfilled (已成功)通常也称为 resolved
    3.rejected (已失败)
    特点
    1.对象状态不受外界影响。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态
    2.一旦改变状态,就不会在改变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected
    缺点
    1.无法取消Promise,一旦新建它就会立即执行,无法中途取消。
    2.如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
    3.当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

    我们看一个简单的promise例子

    Promise构造函数接受一个函数,该函数有两个参数 resolve 和 reject,他们也是两个函数。
    resolve函数的作用是:将Promise 的状态从“未完成”变成“成功”(即从“pending”变成“resolved”),在异步操作成功的时调用,并将一部操作的结果,作为参数传递出去。
    reject函数的作用是:将Promise 的状态从“未完成”变成“失败”(即从“pending”变成“rejected”),在异步操作成功的时调用,并将一部操作的结果,作为参数传递出去。
    Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

    let promise = new Promise((resolve,reject)=>{
        // 接收一个callback。参数是成功函数与失败函数
        setTimeout(()=>{
           let num = parseInt(Math.random()*100);
           // 如果数字大于50就调用成功的函数,并且将状态变成Resolved
           if(num > 50){
              resolve(num);
           }else{
            // 否则就调用失败的函数,将状态变成Rejected
              reject(num)
           }
        },3000)
    })
    

    当Promise执行的内容符合你预期的成功条件的话,就调用resolve函数,失败就调用reject函数,这两个函数的参数会被promise捕捉到。可以在之后的回调中使用
    创建一个承诺完成了,我们如何使用它呢

    promise.then(res => {
      //在构造函数中如果你执行力resolve函数就会到这一步
      console.log(res)
    }, err => {
      // 执行了reject函数会到这一步
      console.log(err);
    })
    

    Promise.prototype.then()

    then方法接収两个函数,第一个是承诺成功(状态为resolved)的回调函数,第二个(可选)是承诺失败(状态为rejected)的回调函数。
    then方法的返回值不是一个promise对象就会被包装成一个promise对象,所以then方法支持链式调用。
    then方法的可以帮我们串行的解决一些逻辑问题,让我们的书写更加顺畅。
    看看下面这个

    ajax('first');
    ajax('second');
    ajax('third');
    需要按顺序来执行怎么办?
    ajax('first').success(function(res){
        ajax('second').success(function(res){
            ajax('third').success(function(res){
                //串行完毕可以执行你想要的内容了
            });
        })
    })
    

    上面地狱式的回调,可怕!!如果使用下面的 then 链式调用,就会好很多

    let promise = new Promise((resolve,reject)=>{
        ajax('first').success(function(res){
            resolve(res);
        })
    })
    promise.then(res=>{
        return new Promise((resovle,reject)=>{
            ajax('second').success(function(res){
                resolve(res)
            })
        })
    }).then(res=>{
        return new Promise((resovle,reject)=>{
            ajax('second').success(function(res){
                resolve(res)
            })
        })
    }).then(res=>{
        // 串行完毕你要做的xxx可以开始了
    })
    

    串行说完了,那并行的怎么办,当我们有多个异步事件,之间并没有先后顺序,只需要全部完成就可以开始工作。
    这个时候我们可以使用 Promise.all

    Promise.all()

    Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

    const p = Promise.all([p1, p2, p3]);
    

    上面代码中,Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
    p的状态由p1、p2、p3决定,分成两种情况。
    1.只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
    2.只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

    var p1 = new Promise((resolve, reject) => {
        resolve('p1')
    })
    var p2 = new Promise((resolve, reject) => {
        setTimeout(()=> {
             resolve('p1')
        }, 1000)
    })
    var p3 = new Promise((resolve, reject) => {
        resolve('p3')
    })
    Promise.all([p1, p2, p3])
    .then(val => {
        console.log('then', val)
    })
    .catch(err => {
        console.log('catch', err)
    })
    //   then (3) ["p1", "p1", "p3"]
    
    var p1 = new Promise((resolve, reject) => {
        resolve('p1')
    })
    var p2 = new Promise((resolve, reject) => {
        setTimeout(()=> {
             resolve('p1')
        }, 1000)
    })
    var p3 = new Promise((resolve, reject) => {
        reject('p3')
    })
    Promise.all([p1, p2, p3])
    .then(val => {
        console.log('then', val)
    })
    .catch(err => {
        console.log('catch', err)
    })
    //  catch p3
    

    注意: 若实例自带了 .then() 则先执行实例的 then 在执行 all的then;若实例自带了 .catch 则先调用 实例的 catch 再执行 .then()。

    var p1 = new Promise((resolve, reject) => {
        resolve('p1')
    }).then(val => {
        console.log(555, val)
    })
    var p2 = new Promise((resolve, reject) => {
        setTimeout(()=> {
             resolve('p1')
        }, 1000)
    })
    var p3 = new Promise((resolve, reject) => {
        resolve('p3')
    })
    Promise.all([p1, p2, p3])
    .then(val => {
        console.log('then', val)
    })
    .catch(err => {
        console.log('catch', err)
    })
    

    Promise.race()

    Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

    const p = Promise.race([p1, p2, p3]);
    

    上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

    Promise.prototype.catch()

    Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

    var p = new Promise((resolve, reject) => {
        //此处抛出异常,状态就会变成rejected,就会调用catch() 方法的回调函数,处理这个错误。
        throw new Error("测试错误test!!!")  
    })
    p.catch((err) => {
        console.log(err)    // Error: 测试错误test!!!
    })
    

    等同于

    // 写法一
    var p = new Promise((resolve, reject) => {
        throw new Error("测试错误test!!!")  
    })
    p.then(null, (err) => {
        console.log(err)
    })
    // 写法二
    var p = new Promise((resolve, reject) => {
        reject(new Error("错误测试test!!!!"))
    })
    p.catch((err)=> {
        console.log(err)
    })
    // 写法三
    var p = new Promise((resolve, reject) => {
        try {
           throw new Error("错误测试test!!")
        } catch(err) {
            reject(err)
        }
    })
    p.catch((err)=> {
        console.log(err)
    })
    

    Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
    一般来说,不要在then方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法比较好。

    // 不建议使用
    var p = new Promise((resolve, reject) => {
        throw new Error("test-error")
    })
    p.then(val=> {
        console.log(1212)
    }, error => {
        console.log(666, error);
    })
    
    // 建议使用
    var p = new Promise((resolve, reject) => {
        throw new Error("test-error")
    })
    p.then(val=> {
        console.log(1212)
    }).catch(error=> {
        console.log("error", error)
    })
    

    Promise.prototype.finally()

    不管Promise最后状态如何,都会执行 finally() 。
    finally() 方法的回调函数不接受任何参数。这也就意味着finally中无法知道 Promise 的状态。这表明,finally里面的操作应该和状态无关,不依赖与Promise 的执行结果。
    finally的本质是 then 方法的特例
    finally 方法不一定是最后一环,后面还可以在跟 .then() ,返回的是一个Promise

    var p = new Promise((resolve, reject)=> {
        throw new Error("test~~")
    })
    p.then((val)=> {
        console.log(11, val)
    }).finally(()=> {
        console.log('finally')
    }).catch((err)=> {
        console.log(err)
    })
    //  finally
    //  Error: test~~
    

    还有

    Promise.allSettled()

    Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。

    Promise.any()

    ES2021 引入了Promise.any()方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。
    Promise.any()跟Promise.race()方法很像,只有一点不同,就是不会因为某个 Promise 变成rejected状态而结束。

    注意:
    all(), race(), allSettled(), any() 都是 Promise 的方法;
    then(), finally(), catch() 都是Promise实例的方法挂载在Promise.prototype上;

    Promise和async/await(二)

    相关文章

      网友评论

        本文标题:Promise和async/await(一)

        本文链接:https://www.haomeiwen.com/subject/lirgqktx.html