Promise 真的懂了吗?

作者: woow_wu7 | 来源:发表于2018-04-06 18:09 被阅读857次

    (1) var

    • var在函数外定义,是全局变量,函数内可以使用
    • var在函数内定义,是局部变量,函数外不能使用
    • var 在其他代码块中定义,是全局变量 ----- 如 if 等
    var x = 10;
    
    ( 
      function fun() {
         var y = 20;
         console.log(x)      // 10
      } 
    )();
    
    console.log(y)    // 报错 y is not defined     ----- var在函数内部定义,是局部变量,函数外无法读取
    
    
    
    -------------------------------------
    
    if (true) {
        var z = 300;
    }
    console.log(z)    // 300       // var在其他代码块中定义,是全局变量
    
    
    

    (2) 对象es6

    • 表达式 - 可以作为对象的属性名和方法名
    • 变量名可以直接作为对象属性名
    • 对象中的方法可以简写
    let ani = 'animal'
    let nam = 'name'
    
    
    let newAr = {
       ani // 变量名直接作为对象的属性名,等同于 ( ani: ani 即 ani: animal) 前者是字符串,后者ani是变量
       nam,
       [ani + nam] : 2000,   // 中括号中写表达式,作为对象的属性名或方法名
       getAge() {            // 方法名简写,相当于 getAge: funciton() { console.log(this.animalname) }
          console.log(this.animalname)
       }
    }
    
    
    newAr.getAge();
    console.log(newAr,'newAr')
    
    








    Promise

    Promise含义

    • promise是一种异步编程的解决方案
    • 比传统的 回调函数和事件 更强大

    Promise具体是什么?

    • promise是一个容器,里面保存着某个未来才会结束的事件 (比如 异步操作的结果)
    • 语法上:promise是一个对象,可以从它获取异步操作的消息
    • promise提供统一的api,所以各种异步操作都可以用同样的方法进行处理

    Promise对象的特点?

    • promise对象的状态不受外界影响 ----- ( promise有三种状态 )
      pending进行中,fulfilled已成功,rejected已失败
      只有异步操作的结果可以决定当前是哪一种状态,其他别的操作无法改变这个状态
    • promise的状态一旦改变,就不会再变,任何时候都能得到这个结果
      promise状态改变只有两种情况: pending状态 变为 fulfilled状态pending状态 变为 rejected状态
      只要这两种情况发生,状态就凝固了,不会再改变,称为 ( resolved ) 已定型
      例如:只要改变已经发生了,你再对promise对象添加回调函数,也会立即得到这个结果

    Promise vs 事件?

    Promise:只要改变已经发生了,你再对promise对象添加回调函数,也会立即得到这个结果
    event----: 如果改变发生了,你再去监听,得不到结果

    Promise对象 的优缺点?

    • 优点
      promise对象,可以将 异步操作同步操作的流程 表达出来,避免层层嵌套
    • 缺点
      (1) promise一旦新建,就会立即执行,无法取消
      (2) 如果不设置回掉函数,promise内部抛出的错误就不会反应到外部
      (3) 处于pending状态时,是不能知道目前进展到哪个阶段的 ( 刚开始?,即将结束?)

    Promise构造函数

    es6规定,Promise对象 是一个构造函数,用来生成Promise实例

    • Promise构造函数,接受一个函数作为参数,该函数有两个参数分别是 resolve 和
      reject,它们是两个函数
    • resolve 和 reject 由 javascript 引擎提供,不用自己部署
    • resolve函数
      resolve函数的作用是,将Promise对象的状态,由pending=>变为fulfilled,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
    • reject函数
      reject函数的作用是,将Promise对象的状态,由pending=>变为rejected,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
    
    const promise = new Promise(function(resolve, reject) {
      // ... some code
    
      if (/* 异步操作成功 */){
        ... 异步操作
        resolve(value);  //  异步操作的结果作为参数,传递出去
      } else {
        reject(error);
      }
    });
    
    
    • .then 方法
      (1) promise实例生成以后,可以用.then方法,分别指定resolved状态 和 rejected状态的回调函数
      (2) .then方法可以接受 ( 两个回调函数 ) 作为参数,
      ===> 第一个回调函数,在promise对象的状态变为 resolved时调用 ------ (该回调函数在promise构造函数中定义,在实例化后,.then方法中调用 )
      ===> 第二个回调函数,在promise对象的状态变为 rejected 时调用 ------ (该回调函数在promise构造函数中定义,在实例化后,.then方法中调用 )
      (3) 第二个回调是可选的,可以不提供
      (4) 这两个函数,都接受 promise对象 传出的值 作为参数
    
    promise.then(function(value) {   
    
      // success
      // 该回调函数在promise对象状态变为resolved时调用,参数是promise对象 resolved时,传出的值
    
    }, function(error) {
      // failure
    });
    
    

    很重要的一个例子

    //很重要的一个例子
    
    
    function timeout(ms) {
      return new Promise((resolve, reject) => {
        setTimeout(resolve, ms, 'done');
      });
    }
    
    timeout(100).then((value) => {
      console.log(value);
    });
    
    
    上面代码中,timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果。
    
    过了指定的时间(ms参数)以后,Promise实例的状态变为resolved,就会触发then方法绑定的回调函数。
    
    

    promise新建后会立即执行

    // promise新建后会立即执行
    
    
    
    let promise = new Promise(function(resolve, reject) {
      console.log('Promise');
      resolve();  // 这里没有传参,作用是去触发.then中的回调函数
    
         // resovle()函数的作用,是把promise实例对象的状态变为resolved,  --------
         // 在异步操作成功时调用,并将异步操作的结果作为参数传递出去
    
         // 而当promise实例对象的状态变为 resolved时,就会触发 .then函数中的 回调函数   --------
    });
    
    promise.then(function() {
      console.log('resolved.');
    });
    
    console.log('Hi!');
    
    // Promise                   // 注意执行顺序
    // Hi!
    // resolved
    
    
    
    上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。
    
    然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。
    
    

    ( 请求图片 ) https://blog.csdn.net/h1534589653/article/details/77528367
    ( Image对象 ) https://blog.csdn.net/baihuaxiu123/article/details/53091105

    异步加载图片

      componentDidMount() {
            
            function loadImage(url) {
                return new Promise((resolve, reject) => {
                    const image = new Image('400', '200')   // 生成image实例对象,宽高
                    image.onload = () => {
                        resolve(image)               // 加载成功时候,返回image对象
                    }
                    image.onerror = () => {
                        reject( new Error(`could not load image at ${url}`))   // 加载失败是报错
                    }
                    image.src = url   // 请求的图片地址
                })
            }
    
    
            loadImage('http://pic.7y7.com/201410/2014102458431393_600x0.jpg')
                .then(res => {
                    console.log( res.src )   // 拿到image对象的src 属性
                    this.setState({
                        images: res
                    },() => {console.log(this.state.images)})
                },  () => {
                        // rejected状态下的回掉函数
                })
                
        } 
    

    resolve函数,reject函数 ------------- (重要)

    • 如果调用resolve和reject函数时,带有参数,那么他们的参数会传给回调函数
    • reject函数 的 参数, 通常是 Error对象的实例,表示抛出的错误
    • resolve函数的参数,除了正常值以外,还可能是另一个promise实例
    
    const p1 = new Promise(function (resolve, reject) {
      // ...
    });
    
    const p2 = new Promise(function (resolve, reject) {
      // ...
      resolve(p1);       // p2异步操作的结果返回 --> p1 异步操作
    })
    
    
    上面代码中,p1和p2都是 Promise 的实例,
    
    但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作。
    
    
    
    ----------------------------------------------------
    
    
    注意: 上面例子中
    
    p1的状态决定了p2的状态
    
    (1) 如果 p1 的状态是pending,那么 p2 的回调函数就会等待 p1 的状态改变
    (2) 如果 p1 的状态是 fulfilled或者rejected,那么 p2 的回调函数将会立即执行
    
    

    Promise嵌套 ---------------------------(重要)(重要)(重要)

    
    
    componentDidMount() {
            let i = 0
            setInterval(() => {
                console.log(`经过了${++i}s`)
            },1000)
    
            const p1 = new Promise( (resolve,reject) => {
                setTimeout(() => {
                    reject(new Error('fail'))
                    console.log('3s')  // console.log语句仍然会执行,并且在reject()异步函数 前执行
                },3000)
            })
            const p2 = new Promise( (resolve,reject) => {
                setTimeout( () => {
                    return resolve(p1)   // 一般都在这里加return,这样后面的代码就不会执行,防止意外!!
                    console.log('1s')
                }, 1000 )
            })
    
            p2.then(res => console.log(res))    // 并没有执行
              .catch(error => console.log(error))
    
        // 注意: p2.then(res => console.log(....))并没有执行,因为p2的状态变成了p1的状态,是rejected
        // p2.then(res => console.log(res,'fulfilled'), res => console.log(res,'rejected'))
        // 实际执行的是上面的 第二个回调函数
    }
    
    
    解析:
    (1) p1 是一个promise对象,3s后状态变为rejected
    (2) p2 是一个promise对象,状态在 1s 后改变,但是P2的resolvef方法返回的是p1,p1是promise对象
    
                   导致p2的状态由p1决定,即 p1的状态传递给p2
    
    (3) P2会等待P1的状态改变为 fulfilled或者reject,P1状态改变后,P2的回调函数会立刻执行 ( --!!!重要!!!-- )
    
                   ( 所以1s的时候,.then方法并没有输出内容 )
          (并且3s后,p2的状态不是fulfilled,而是 rejeced,即是p1的状态 )
    
    (4) 又过了2s,p1的状态变为 rejected,导致触发 .catch 回调函数
    
    
    
    • 一般来说,调用resolve或reject以后,Promise 的使命就完成了,后继操作应该放到then方法里面,而不应该直接写在resolve或reject的后面。所以,最好在它们前面加上return语句,这样就不会有意外。

    Promise.prototype.then() 方法

    promise实例有.then方法,是定义在原型对象 Promise.prototype 上的

    • then() 方法的作用是为 promise实例添加状态改变后的回调函数
    • then() 方法的参数是两个回掉函数,第一个是 resolved 状态的回调函数,第二个是rejected状态的回调函数 ( 第二个参数可选,一般都不用,而用 catch()方法捕获错误 )
    • then() 方法返回的是新的promise实例,因此可以采用链式写法

    Promise.prototype.then()方法的链式调用---------(重要)

    采用链式的then,可以指定一组按照次序调用的回调函数。

    • 这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。
    
    
    componentDidMount() {
            let i = 0
            setInterval(() => {
                console.log(`经过了${++i}s`)
            },1000)
        
            const lian1 = new Promise( (resolve,reject) => {
                return setTimeout(() => {
                    resolve('2s的promise的fulfilled状态返回值')
                },2000)
            })
            lian1
            .then(res => console.log(res))
            .then( res => {
                return new Promise( (resolve,reject) => {
                    return  setTimeout(() => {
                        return reject('3s的promise的rejected状态返回值')
                    },1000)
                })
            })
            .then(res => console.log(res,'reject'), res => console.log(res, 'reject'))
    }
    
    //  经过了1s
    //  经过了2s
    //  2s的promise的fulfilled状态返回值
    //  经过了3s
    //  3s的promise的fulfilled状态返回值 reject
    // 经过了4s
    

    Promise.prototype.catch()

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

    • 如果promise实例对象的状态变为rejected,就会触发 catch() 方法指定的回调函数
    • 如果 .then() 方法指定的回调函数在运行中抛出错误,也会被 catch() 方法捕获
    • promise对象的错误具有冒泡性质,会一直向后传递,直到被捕获为止
      ( 也就是说错误总是会被下一个catch语句捕获 )
    • 一般来说,不要在.then()方法中定义rejected状态的回调函数,而总是使用 .catch()方法
    • 一般总是建议,promise对象后要跟 catch()方法,这样可以处理 promise内部发生法的错误,catch() 方法返回的还是promise对象,因此后面还可以接着调用 then() 方法
    • catch() 方法中还能再抛错误,如果 catch()方法抛出错误后,后面没有catch()方法,错误就不会被捕获,也不会传递到外层。如果catch()方法抛出错误后,后面有then()方法,会照常执行,后面有catch()方法,错误还会被再一次捕获
    
    p.then((val) => console.log('fulfilled:', val))
     .catch((err) => console.log('rejected', err));
    
    // 等同于
    
    p.then((val) => console.log('fulfilled:', val))
     .then(null, (err) => console.log("rejected:", err));
    
    

    例子

    
    getJSON('/post/1.json')
      .then(function(post) {
          return getJSON(post.commentURL);
          }) 
      .then(function(comments) {
          // some code
        })
      .catch(function(error) {
          // 处理前面三个Promise产生的错误
    });
    
    
    上面代码中,一共有三个 Promise 对象:一个由getJSON产生,两个由then产生。
    
    它们之中任何一个抛出的错误,都会被最后一个catch捕获。
    
    

    promise.prototype.finally()

    promise.prototype.finally()方法用于指定不管promise对象最后的状态如何,都会执行的操作

    • finally() 方法的回调函数,不接受任何参数。
      ( 这就意味着,无法知道前面pormise实例对象最后的状态是fulfilled还是rejected,也就是说,finally()函数中的操作与状态无关,不依赖promise对象执行的结果 )
    • finally总是会返回之前的值
    
    promise
    .then(result => {···})
    .catch(error => {···})
    .finally(() => {···});
    
    
    
    上面代码中,不管promise最后的状态,在执行完then或catch指定的回调函数以后,
    
    都会执行finally方法指定的回调函数。
    
    

    Promise.all() -------- 注意,不在原型对象上

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

    • promise.all() 方法的参数可以不是数组,但是必须具有 iterator 接口,且返回的每个成员都是promise实例 ( 具有iterator接口,就是可遍历的数据结构,可以被 for...of 遍历 )
    • 注意:如果作为参数的promise实例 ( 即Promise.all()实例子是rejected状态 )自己定义了catch()方法,就不会触发Promise.all()实例的 catch() 方法
    
    const p = Promise.all( [p1, p2, p3] );
    
    
    上面代码中,promise.all()方法,接受一个数组作为参数,p1, p2, p3都是promise实例
    
    promise.all() 方法的参数可以不是数组,但是必须具有 iterator 接口
    
    
    p的状态由 p1, p2, p3决定,分两种情况
    
    (1) 只有p1,p2,p3的状态都变为 fulfilled, p的状态才会变为 fulfilled
        此时,p1,p2,p3的返回值组成一个数组,传递给p的回调函数
    
    (2) 只要p1,p2,p3中有一个被 rejected,p的状态就变成rejected
        此时,第一个被rejected的实例的返回值,会传给p的回调函数
    
    

    例子

    情况1:
    
    // a,b,c都是promise实例对象
    // 当a,b,c都是fulfilled状态时, p 才是fulfilled状态,才会触发then的resolved状态的回调函数
    // p 的回调函数的参数,是a,b,c都变为resolved状态时的返回值组成的数组
    
    componentDidMount() {
            let a = new Promise((resolve, reject) => {
                return resolve(1)
            })
            let b = new Promise((resolve,reject) => {
                return resolve(2)
            })
            let c = new Promise((resolve,reject) => {
                return resolve(3)
            })
    
            const p = Promise.all([a,b,c])     // a,b,c都是promise实例对象
    
            p.then(res => console.log(res))  // 输出 [1, 2, 3]
    }
    
    情况2:
    
    // a,b,c都是promise实例对象
    // 当a,b,c中有一个是rejected状态时,p的状态就是rejected状态,
    // p 的回调函数的参数,是最先被rejected的Promse实例的返回值
    
    componentDidMount() {
            let a = new Promise((resolve, reject) => {
                // return resolve(1)
                return reject(new Error('错误来自promise----a'))
            })
            let b = new Promise((resolve,reject) => {
                // return resolve(2)
                return reject(new Error('错误来自promise----b'))
            })
            let c = new Promise((resolve,reject) => {
                return resolve(3)
            })
    
            const p = Promise.all([a,b,c])  // p是rejected时,p的回调函数的参数是最先rejected的实例返回值
    
            p.then(res => console.log(res))
             .catch(err => console.log(err))  // 输出    Error: 错误来自promise----a
    }
    

    特殊情况

    
    const p1 = new Promise((resolve, reject) => {
      resolve('hello');
    })
    .then(result => result)
    .catch(e => e);
    
    const p2 = new Promise((resolve, reject) => {
      throw new Error('报错了');
    })
    .then(result => result)
    .catch(e => e);
    
    Promise.all([p1, p2])
    .then(result => console.log(result))
    .catch(e => console.log(e));
    // ["hello", Error: 报错了]
    
    
    
    上面代码中,p1会resolved,p2首先会rejected,
    
    但是p2有自己的catch方法,该方法返回的是一个新的 Promise 实例,p2指向的实际上是这个实例。
    
    该实例执行完catch方法后,也会变成resolved,导致Promise.all()方法参数里面的两个实例都会resolved,
    
    因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数。
    
    

    Promise.race() --------- 对比Promise.all()

    Promise.race()方法的作用同样是将多个promise对象实例包装成新的promise实例

    • race是赛跑,率先的意思
    • 规则: 如果p1, p2, p3 中实例对象的状态有一个率先改变, p的状态就会跟这改变 ( 无论是变为fulfilled状态,还是变为 rejected状态 ),p的回调函数的参数,是最先改变的那个promise实例的返回值
    
    const p = Promise.race([       // Promise.race() 只要有一个参数状态改变,p的状态就是跟着改变
      fetch('/resource-that-may-take-a-while'),    // fetch返回的是promise对象
      new Promise(function (resolve, reject) {     // 5s后变为rejected状态
        setTimeout(() => reject(new Error('request timeout')), 5000)
      })
    ]);
    
    p
    .then(console.log)
    .catch(console.error);
    
    
    
    解析:
    fetch在5s钟内请求成功,p变成fulfilled状态,触发p的then()方法
    
    fetch在5s中内请求失败,p变为rejected状态,触发p的catch()方法
    
    

    Promise.resolve()

    promise.resolve()可以将对象转换为promise对象

    • promise.resolve('foo') 等价于 new Promise(resolve => resolve('foo'))
    
    Promise.resolve('foo')
    // 等价于
    new Promise(resolve => resolve('foo'))
    
    

    Promise.resolve()方法的 参数

    总结:
    Promise.resolve()的参数无论是什么类型 ( 或者不带参数,或者参数本来就是一个promise对象,或者参数是一个thenable对象,或者参数是原始值,或者参数是普通对象 )本质上Promise.resolve()都会把参数转化为Promise对象,只是状态分情况而已,比如:如果参数是thenable对象,会立即执行thenable对象的then方法,状态当然后then方法中的函数决定,从而决定Promise.resolve()返回的promise对象的状态
    Promise.resolve()方法的参数分为四种情况

    • 参数是一个promise实例对象
      如果Promise.resolve()方法的参数是一个( 实例对象 ),那么Promise.resolve()方法将原封不动的( 返回这个实例对象 )
    
    componentDidMount() {
            const foo = new Promise( (resolve, reject) => {
                return resolve('foo是一个promise实例对象')
            })
            const p = Promise.resolve(foo)     // Promise.resolve()的参数是一个promise实例
            console.log( p );    
    
        // 输出: Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "foo是一个promise实例对象"}
    
    }
    
    
    • 参数是一个thenable 对象
      如果Promise.resolve()方法的参数是 ( thenable对象 ),那么Promise.resolve()方法会将这个对象转化为promise对象,然后立刻执行 thenable对象的 then 方法
    什么是 thenable 对象 ?
    
    let thenable = {
      then: function(resolve, reject) {
        resolve(42);
      }
    };
    
    
    // 如果Promise.resolve()的参数是 thenable对象, Promise.resolve()方法会把thenable对象转化为promise对象
    // 然后立刻执行thenable对象的then方法
    
    
    componentDidMount() {
            const thenable = {    // thenable对象,里面有then方法
                then: (resolve,reject) => resolve('这是thenable对象')
            }
            Promise.resolve(thenable)   // 参数是thenable对象,立刻执行thenable对象的then方法
                .then(res => console.log(res))  // thenable对象的的状态是fulfilled,输出其返回值
    }
    
    // 输出:这是thenable对象
    
    • 参数不是thenable对象,或者根本不是一个对象
      如果参数是一个原始类型的值( 数字,字符串,布尔值 ),或者是一个不具有then方法的对象,则Promise.resolve()方法返回一个新的promise对象,状态是fulfilled
    
     componentDidMount() {
        const str = 'abc'    
        const foo = Promise.resolve(str)
        // 参数是原始类型的值,Promise.resolve()方法会返回一个promise对象,状态是resolved
                
        foo.then(res => console.log(res)) // 所以该回调会执行
    }
    
    • 不带参数
      当Promise.resolve()方法不带参数是,直接返回一个promise对象,状态是resolved

    Promise.reject()方法

    Promise.reject()方法返回一个promise实例对象,状态是rejected

    • 和Promise.resolve()类似,只是Promise.rejected()方法的状态一定是rejected
    
    const p = Promise.reject('出错了');
    p.then(null, function (s) {
      console.log(s)
    });
    // 出错了
    
    
    // 等同于
    
    
    const p = new Promise((resolve, reject) => reject('出错了'))
    p.then(null, function (s) {
      console.log(s)
    });
    // 出错了
    
    

    promise对象的应用

    Promise.try()

    
    const f = () => console.log('now');   // 函数f是一个同步事件
    Promise.resolve().then(f);   // 通过Promise.resolve()返回一个promise对象,状态是resolved,f变成异步
    console.log('next'); 
    
    // next       所以会先输出next,再输出now
    // now
    
    
    如何让同步函数同步执行,异步函数异步执行,并且让他们具有统一的api呢?
    • (1) 使用async函数

    await 操作符用于等待一个 Promise 对象, 它只能在异步函数 async function 内部使用.

    
    componentDidMount() {
            const funSync1 = () => console.log('我是同步函数1111111')
            const funSync2 = () => console.log('我是同步函数2222222')
    
            const funAsync = async () => {     // async关键字,定义的函数是异步函数,返回promise对象
                await funSync1()
            }
            funAsync().then(funSync2())
            console.log('bbbb')
    }
    
    // 先把两个同步函数变成了异步,在异步函数中,先执行funSync1,后执行funSync2
    // 使用async关键字后,会把同步包装成的异步函数,按同步方式执行
    // 所以最后得到的输出顺序是:
    
    
    // 我是同步函数1111111
    // 我是同步函数2222222
    // bbbb
    
    • (2) 使用 new Promise()

    
    const f = () => console.log('now');
    (
      () => new Promise(
        resolve => resolve(f())
      )
    )();
    console.log('next');
    
    // now
    // next
    
    
    • (3) Promise.try() 提案

    
    const f = () => console.log('now');
    Promise.try(f);
    console.log('next');
    
    // now
    // next
    
    

    相关文章

      网友评论

        本文标题:Promise 真的懂了吗?

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