zoukankan      html  css  js  c++  java
  • JavaScript Promise 对象

    一、概念

    Promise 是一个对象,也是一个构造函数。
    Promise 对象代表了未来将要发生的事件,用来传递异步操作的消息。

    二、特点

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

    1. 有三种状态:
    pending(进行中),
    fulfilled(已成功),
    rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,
    任何其他操作都无法改变这个状态。
    
    2. 有了Promise对象,就可以将异步操作以同步操作的流程表达出来,
    避免了层层嵌套的回调函数。
    
    3. Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行
    当处于pending状态时,无法得知目前进展到哪一个阶段
    
    
    
    

    三、基本用法

    Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。
    它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
    
    resolve函数的作用是,将Promise对象的状态变成成功
    reject函数的作用是,将Promise对象的状态变成失败
    
    function timeout(ms) {
      return new Promise((resolve, reject) => {
        console.log('立即执行');    // Promise 新建后就会立即执行。这一行先输出;
        setTimeout(resolve, ms, 'done');
      });
    }
    
    timeout(100).then((value) => {
      console.log(value);
    });
    timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果。
    过了指定的时间(ms参数)以后,Promise实例的状态变为resolved,
    就会触发then方法绑定的回调函数。
    
    1.调用resolve或reject并不会终结 Promise 的参数函数的执行。
     resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务
    new Promise((resolve, reject) => {
      resolve(1);
      console.log(2);
    }).then(r => {
      console.log(r);
    });
    // 2
    // 1
    一般来说,调用resolve或reject以后,Promise 的使命就完成了,后继操作应该放到then方法里面,
    而不应该直接写在resolve或reject的后面。所以,最好在它们前面加上return语句,这样就不会有意外。
    new Promise((resolve, reject) => {
      return resolve(1);
      // 后面的语句不会执行
      console.log(2);
    })
    
    

    四、Promise.prototype.then()

    Promise 实例具有then方法,也就是说,then方法是定义在原型对象上
    它的作用是为 Promise 实例添加状态改变时的回调函数。
    then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

    then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

    getJSON("/posts.json").then(function(json) {
      return json.post;
    }).then(function(post) {
      // ...
    });
    采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,
    有可能返回的还是一个Promise对象(即有异步操作),
    这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。
    

    五、Promise.prototype.catch()

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

    getJSON('/posts.json').then(function(posts) {
      // ...
    }).catch(function(error) {
      // catch会捕获 getJSON()方法中的错误 和 then()方法指定的回调函数中的错误;
     
      console.log('发生错误!', error);
    
    });
    
    1.  如果 Promise 状态已经变成resolved,再抛出错误是无效的。
    
    const promise = new Promise(function(resolve, reject) {
      resolve('ok');
      throw new Error('test');
    });
    promise
      .then(function(value) { console.log(value) })
      .catch(function(error) { console.log(error) });
    // ok
    
    2.  Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。
    也就是说,错误总是会被下一个catch语句捕获。
    
    getJSON('/post/1.json').then(function(post) {
      return getJSON(post.commentURL);
    }).then(function(comments) {
      // some code
    }).catch(function(error) {
      // 处理前面三个Promise产生的错误
    });
    
    一共有三个 Promise 对象:一个由getJSON()产生,两个由then()产生。它们之中任何一个抛出的错误,都会被最后一个catch()捕获。
    
    一般来说,不要在then()方法里面定义 Reject 状态的回调函数(即then的第二个参数),
    总是使用catch方法。
    
    
    

    六、Promise.prototype.finally()

    finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。
    该方法是 ES2018 引入标准的。
    finally方法的回调函数不接受任何参数,这意味着没有办法知道,
    前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。

    七、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 实例。

     const p = Promise.all([p1, p2, p3]);
    
    (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
    
    (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
    
    
    // 如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发
    // Promise.all()的catch方法。
    
    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: 报错了]
    
    
    
    
    

    八、Promise.race()

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

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

    九、Promise.allSettled()

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

    const resolved = Promise.resolve(42);
    const rejected = Promise.reject(-1);
    
    const allSettledPromise = Promise.allSettled([resolved, rejected]);
    
    allSettledPromise.then(function (results) {
      console.log(results);
    });
    // [
    //    { status: 'fulfilled', value: 42 },
    //    { status: 'rejected', reason: -1 }
    // ]
    
    上面代码中,Promise.allSettled()的返回值allSettledPromise,
    状态只可能变成fulfilled。它的监听函数接收到的参数是数组results。
    该数组的每个成员都是一个对象,对应传入Promise.allSettled()的两个 Promise 实例。
    每个对象都有status属性,该属性的值只可能是字符串fulfilled或字符串rejected。
    fulfilled时,对象有value属性,rejected时有reason属性,对应两种状态的返回值。
    
    

    十、Promise.resolve()

    有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用。
    const jsPromise = Promise.resolve($.ajax('/whatever.json'));

    • Promise.resolve()方法的参数分成四种情况。
    1. 参数是一个 Promise 实例
        如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。
    
    2. 参数是一个thenable对象
        Promise.resolve()方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法。
    
    3. 参数不是具有then()方法的对象,或根本就不是对象
        如果参数是一个原始值,或者是一个不具有then()方法的对象,则Promise.resolve()方法返回一个新的 Promise 对象,状态为resolved。
    
    
    4. 不带有任何参数
        Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
        如果希望得到一个 Promise 对象,比较方便的方法就是直接调用Promise.resolve()方法。
        立即resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。
    
    
    

    十一、Promise.reject()

    Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
    Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。

    Promise.reject('出错了')
    .catch(e => {
      console.log(e === '出错了')
    })
    // tru
    

    十二、Promise.try()

    实际开发中,经常遇到一种情况:不知道或者不想区分,函数f是同步函数还是异步操作,
    但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,
    都用then方法指定下一步流程,用catch方法处理f抛出的错误。一般就会采用下面的写法。
    
    Promise.resolve().then(f)
    上面的写法有一个缺点,就是如果f是同步函数,那么它会在本轮事件循环的末尾执行
    
    const f = () => console.log('now');
    Promise.resolve().then(f);
    console.log('next');
    // next
    // now
    
    

    那么有没有一种方法,让同步函数同步执行,异步函数异步执行,
    并且让它们具有统一的 API 呢?回答是可以的,并且还有两种写法。
    第一种写法是用async函数来写。

    const f = () => console.log('now');
    (async () => f())();
    console.log('next');
    // now
    // next
    
    
    提供Promise.try方法替代上面的写法。
    
    const f = () => console.log('now');
    Promise.try(f);
    console.log('next');
    // now
    // next
    
    
    

    例子

    • 异步代码链式调用
    console.log('开始');
        new Promise(resolve=>{
            setTimeout(()=>{
                resolve('hello');
                
            },2000)
           
        })
        .then(value=>{
            console.log(value)      // hello
            return new Promise(resolve=>{
                setTimeout(()=>{
                    resolve('world');
                },500)
            })
        })
        .then(value=>{
            console.log(value +  'world');
        })
        .catch(err=>{
            console.log('catch捕获错误',err);
        })
        .finally(()=>{
            console.log('执行结束');
        })
        // 开始
        // hello 
        // worldworld
        // 执行结束
    
    
  • 相关阅读:
    模板为webpack的目录结构
    实例和内置组件
    微信小程序之富文本解析
    微信小程序获取输入框(input)内容
    for循环的语法和执行顺序
    循环
    选择结构(二)
    选择结构
    算术运算
    变量
  • 原文地址:https://www.cnblogs.com/kgwei520blog/p/13815540.html
Copyright © 2011-2022 走看看