美文网首页
【阮一峰ES6】——Promise

【阮一峰ES6】——Promise

作者: zzyo96 | 来源:发表于2020-12-21 09:43 被阅读0次
    1.promise的含义

    Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

    所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。

    2 Promise对象有以下两个特点。
    • Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态

    • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了

    3 基本用法
    const promise = new Promise(function(resolve, reject) {
      // ... some code
    
      if (/* 异步操作成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    });
    
    promise.then(function(value) {
      // success
    }, function(error) {
      // failure
    });
    

    一个简单的栗子:

    function timeout(ms) {
      return new Promise((resolve, reject) => {
        setTimeout(resolve, ms, 'done');
      });
    }
    
    timeout(100).then((value) => {
      console.log(value);
    });
    

    下面是异步加载图片的栗子

    function loadImageAsync(url) {
      return new Promise(function(resolve, reject) {
        const image = new Image();
    
        image.onload = function() {
          resolve(image);
        };
    
        image.onerror = function() {
          reject(new Error('Could not load image at ' + url));
        };
    
        image.src = url;
      });
    }
    

    一个ajax 栗子。。

    const getJSON = function(url) {
      const promise = new Promise(function(resolve, reject){
        const handler = function() {
          if (this.readyState !== 4) {
            return;
          }
          if (this.status === 200) {
            resolve(this.response);
          } else {
            reject(new Error(this.statusText));
          }
        };
        const client = new XMLHttpRequest();
        client.open("GET", url);
        client.onreadystatechange = handler;
        client.responseType = "json";
        client.setRequestHeader("Accept", "application/json");
        client.send();
    
      });
    
      return promise;
    };
    
    getJSON("/posts.json").then(function(json) {
      console.log('Contents: ' + json);
    }, function(error) {
      console.error('出错了', error);
    });
    
    4. resolve和reject除了正常的值外, 还可能是另一个promise实例

    比如这样:

    const p1 = new Promise(function (resolve, reject) {
      // ...
    });
    
    const p2 = new Promise(function (resolve, reject) {
      // ...
      resolve(p1);
    })
    

    p2 resolve返回的是p1,这时p2的状态完全 由p1决定

    调用resolve或reject并不会终结 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);
    })
    
    5. Promise.prototype.then()

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

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

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

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

    getJSON("/post/1.json").then(function(post) {
      return getJSON(post.commentURL);
    }).then(function (comments) {
      console.log("resolved: ", comments);
    }, function (err){
      console.log("rejected: ", err);
    });
    
    6. Promise.prototype.catch()

    用于指定发生错误时的回调函数。

    getJSON('/posts.json').then(function(posts) {
      // ...
    }).catch(function(error) {
      // 处理 getJSON 和 前一个回调函数运行时发生的错误
      console.log('发生错误!', error);
    });
    

    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方法。

    // bad
    promise
      .then(function(data) {
        // success
      }, function(err) {
        // error
      });
    
    // good
    promise
      .then(function(data) { //cb
        // success
      })
      .catch(function(err) {
        // error
      });
    

    一般总是建议,Promise 对象后面要跟catch()方法,这样可以处理 Promise 内部发生的错误。catch()方法返回的还是一个 Promise 对象,因此后面还可以接着调用then()方法。

    const 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
    

    上面代码运行完catch()方法指定的回调函数,会接着运行后面那个then()方法指定的回调函数。如果没有报错,则会跳过catch()方法。

    7.Promise.prototype.finally()

    finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

    promise
    .then(result => {···})
    .catch(error => {···})
    .finally(() => {···});
    
    promise
    .finally(() => {
      // 语句
    });
    
    // 等同于
    promise
    .then(
      result => {
        // 语句
        return result;
      },
      error => {
        // 语句
        throw error;
      }
    );
    

    上面代码中,如果不使用finally方法,同样的语句需要为成功和失败两种情况各写一次。有了finally方法,则只需要写一次。

    8. Promise.all()

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

    image.png

    result1,result2是promise实例

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

    p的状态由p1、p2、p3决定,分成两种情况。

    (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。

    (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

    相关文章

      网友评论

          本文标题:【阮一峰ES6】——Promise

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