美文网首页
关于Promise

关于Promise

作者: LElysion | 来源:发表于2017-03-13 11:59 被阅读0次

    说到异步,怎么说还是得有Promise这玩意

    Promise 的含义

    Promise 是一个保存着某个未来才会结束的事件的结果的容器(对象),提供异步操作的消息

    Promise的特点

    • 它代表一个异步操作,只有异步操作的结果才可以决定他属于Pending(进行中),Resolve(已完成,也称Fulfilled)和Rejected(已失败)
    • 状态改变后就不可更改而且任何时候都可以得到这个结果

    简单来说Promise就是一个保存异步操作事件状态,而且该状态只能通过异步操作进行改变,且仅能改变一次
    而Promise是提供一个对未来的承诺,至于是否实现,只有异步操作的结果才能决定
    (事件(Event)发生后再监听,监听不成功)

    Promise的缺点

    首先,无法取消Promise,一旦新建就会执行,不可取消
    其次,若不设置回调函数,Promise内部抛出的错误不会反馈到外部
    最后,处于Pending(进行中)状态时,无法得知目前发展阶段,即刚开始还是即将完成
    若某些事件不断反复发生,一般来说,使用stream模式比不熟Promise要更好

    Promise

    首先,Promise是一个构造函数,它接受一个函数作为参数,该函数的两个参数分别为resolve和reject,由JavaScript引擎本身提供,用于生成Promise实例
    resolve : 将Promise状态从Pending(进行中)变为Resolved(成功)并且可以将一个参数(大多数时候是异步操作的结果)传出去,
    reject : 将Promise状态从Pending编程Rejected(失败)并将异步操作的错误作为参数传递

    var promise = new Promise(function(resolve, reject){
    //..,some code
    if( /*异步操作成功*/ ){
    resolve(value);
    }else{
    reject(error);
    }
    });
    

    then

    在Promise实例生成后,可以用then方法分别制定Resolved和Reject状态的回调函数

    promise.then(function(value){
    //success
    },function(error){
    //failure
    })
    

    then方法接受两个回调函数作为参数,第一个回调函数式Promise对象的状态变为Resolved时调用,第二个回调函数是Promise对象的状态变为Reject时调用,其中第二个函数使可选的,其中这个两个函数都分别接受来自Promise对象传出的值作为参数

    /*Promise对象的简单例子*/
    function timeout(ms) { //该方法返回一个Promise实例,表示一段时间后才发生的结果
    return new Promise((resolve, reject)=>{
    setTimeOut(resolve, ms, 'done);
    });
    }
    
    timeout(100).then((value) => {
    console.log(value);
    });
    
    let promise = new Promise(function (resolve, reject){
        console.log('i should get you some Promise');
        resolve();
    });//新建的Promise首先执行
    
    promise.then(function(){
        console.log('ok i am Resolved. ');//最后执行then下的回调
    //这里省略了第二个error下的回调
    });
    
    console.log('maybe i will first but the promise will say to me');//其次执行其他函数
    

    Promise.prototype.then()

    then()方法是定义在原型对象Promise.prototype上的,它的作用是为Promise实例添加状态改变时的回调函数,接受两个参数,第一个是Resolved状态的回调函数,第二个函数式Rejected状态的回调函数
    then()方法返回的是一个新的Promise实例(并非原本的Promise实例),因而可以使用链式写法,即then方法再调用另一个then方法

    getJSON("/posts.json").then(function(json) {
      return json.post;
    }).then(function(post) {
      // ...
    });
    

    Promise.prototype.catch()

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

    var promise = new Promise(function(resolve, reject) {
      reject(new Error('test'));
    });
    promise.catch(function(error) {
      console.log(error);
    });
    // Error: test
    

    若Promise状态为Resolved,此时抛出错误并没有用
    Promise对象的错误具有'冒泡'性质,即会一直向后传递,直到被捕获为止,即,错误总是会被下一个catch语句捕获
    一般来说,不要在then方法里面定义Reject状态的回调函数(即then的第二个参数),总是使用catch方法

    // bad
    promise
      .then(function(data) {
        // success
      }, function(err) {
        // error
      });
    
    // good
    promise
      .then(function(data) { //cb
        // success
      })
      .catch(function(err) {
        // error
      });//该方法更好,更接近于try/catch,而且可以捕获前面then方法执行中的错误
    

    需要注意的是,如果没有使用catch方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应

    var someAsyncThing = function() {
      return new Promise(function(resolve, reject) {
        // 下面一行会报错,因为x没有声明
        resolve(x + 2);
      });
    };
    
    someAsyncThing().then(function() {
      console.log('everything is great');
    });
    

    在这里,someAsyncThing函数产生的Promise对象会报错,但是由于没有指定catch方法,所以该错误并不会被捕获,也不会传递到外层代码,导致运行后没有任何输出
    不过Chrome浏览器不遵守该规定,它会抛出错误"ReferenceError: x is not defined"

    var promise = new Promise(function(resolve, reject) {
      resolve('ok');
      setTimeout(function() { throw new Error('test') }, 0)
    });
    promise.then(function(value) { console.log(value) });
    // ok
    // Uncaught Error: test
    

    Node中有一个unhandledRejection事件,专门监听为捕获的reject错误
    ハルノユキ

    process.on('unhandledRejection', function (err, p) {
      console.error(err.stack)
    });
    

    该事件的监听函数有两个参数,第一个是错误对象,第二个是报错的Promise实例,用来了解发生错误的环境信息

    catch方法返回的还是一个Promise对象,所以后面还可以接着调用then方法,当然还可以继续接着catch

    var someAsyncThing = function() {
      return new Promise(function(resolve, reject) {
        // 下面一行会报错,因为x没有声明
        resolve(x + 2);
      });
    };
    
    someAsyncThing()
    .catch(function(error) {
      console.log('oh no', error);
    })
    .then(function() {
      console.log('carry on');
    });
    // oh no [ReferenceError: x is not defined]
    // carry on
    /*如果该Promise中没有报错的话则会直接跳过catch方法*/
    

    Promise.all()

    Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例
    var p = Promise.all([p1,p2,p3]);
    该方法接受一个数组作为参数,p1,p2,p3都是Promise对象实例,如果不是,就会先调用Promise.resolve方法,将参数转为Promise实例,再进一步处理
    (Promise.all()方法的参数可以不是数组,但必须是可以迭代的,具有Iterator接口,且返回的每个成员都是Promise实例)
    p的状态由p1,p2,p3的状态决定,分两种情况
    首先 p1,p2,p3状态都是fulfilled,p的状态才会变成fulfilled,此时p1,p2,p3的返回值组成一个数组,传递给p的回调函数
    其次,p1,p2,p3之中只要有一个被rejected,p的状态就会变成rejected,此时第一个reject的实例的返回值将会传递给p的回调

    // 生成一个Promise对象的数组
    var promises = [2, 3, 5, 7, 11, 13].map(function (id) {
      return getJSON("/post/" + id + ".json");
    });
    
    Promise.all(promises).then(function (posts) {
      // ...
    }).catch(function(reason){
      // ...
    });
    

    Promise.race()

    Promise.race方法同样将多个Promise实例包装成一个新的Promise实例
    var p =Promiee.race([p1,p2,p3])
    该方法中,只要p1,p2,p3之中有一个实例率先改变状态,p的状态就随之改变,而率先改变的Promise实例的返回值就传递给p的回调函数

    var p = Promise.race([
      fetch('/resource-that-may-take-a-while'),
      new Promise(function (resolve, reject) {
        setTimeout(() => reject(new Error('request timeout')), 5000)
      })
    ])
    p.then(response => console.log(response))
    p.catch(error => console.log(error))
    /*该方法中,如果指定时间内无返回结果,变量p的状态就会变成rejected,从而触发catch方法的回调函数*/
    

    Promise.resolve()

    可以将现有对象转为Promise对象
    var jsPromise = Promise.resolve($.ajax('/whatever.json'));
    上述代码将jQuery生成的deferred对象转为一个新的Promise对象
    它等价于Promise.resolve('foo')

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

    根据Promise.resolve方法的参数,可以分为四种情况

    • 参数是一个Promise实例
      没有任何意义,直接返回该实例
    • 参数是一个thenable对象(thenable对象指具有then方法的对象)
      会将该对象转为Promise对象并且立即执行thenable对象中的then方法
    let thenable = { /*thenable对象示例*/
    then: function(resolve, reject){
    resolve(52);
    }
    }
    let p1= Promise.resolve(thenable);
    p1.then(function(value){
    console.log(value); //52 
    })
    
    • 参数不是具有then方法的对象或根本就不是对象
      返回一个新的Promise对象,状态为Resolved
    var p = Promise.resolve('Hello');
    p.then(function (s){
      console.log(s)
    });
    // Hello
    
    • 不带参数
      Promise.resolve是可以不带参数的,这将直接返回一个Resolved状态的Promise对象
    var p = Promise.resolve();
    p.then(function () {
      // ...
    });
    

    Promise.reject()

    Promise.reject(reason) 方法也会返回一个新的Promise实例,该实例的状态为rejected
    该方法中的参数将会作为reject的理由并且成为后续方法的参数

    var p = Promise.reject('出错了');
    // 等同于
    var p = new Promise((resolve, reject) => reject('出错了'))
    
    p.then(null, function (s) {
      console.log(s)
    });
    // 出错了
    

    附加方法

    • done()
      Promise对象的回调链,不管then或是catch结尾,若最后一个方法抛出错误,都有可能无法捕捉
      done()方法总是位于回调链的尾端,保证抛出任何可能出现的错误
    asyncFunc()
      .then(f1)
      .catch(r1)
      .then(f2)
      .done();
    

    实现代码

    Promise.prototype.done = function (onFulfilled, onRejected) {
      this.then(onFulfilled, onRejected)
        .catch(function (reason) {
          // 抛出一个全局错误
          setTimeout(() => { throw reason }, 0);
        });
    };
    
    • finally()
      finally方法用于指定不管Promise对象最后状态如何都会执行的操作,与done不同的是,它接受一个回调函数作为参数,该函数不管如何都必须执行
    server.listen(0)
      .then(function () {
        // run test
      })
      .finally(server.stop);
    

    实现代码

    Promise.prototype.finally = function (callback) {
      let P = this.constructor;
      return this.then(
        value  => P.resolve(callback()).then(() => value),
        reason => P.resolve(callback()).then(() => { throw reason })
      );
    };
    

    参考: http://es6.ruanyifeng.com/#docs/promise

    相关文章

      网友评论

          本文标题:关于Promise

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