Promise笔记

作者: 你隔壁的陌生人 | 来源:发表于2018-03-21 10:12 被阅读0次

    Promise学习(上):

    资料: JavaScript Promise迷你书 

    原著:azu / 翻译:liubin、kaku、honnkyou Version 1.4.1   /  http://liubin.org/promises-book/

    概括笔记:

    一、promise基本概念

    1、Promise是抽象异步处理对象以及对其进行各种操作的组件。

    2、Promise并不是从JavaScript中发祥的概念。Promise最初被提出是在E语言中, 它是基于并列/并行处理设计的一种编程语言。

    3、Promise把类似的异步处理对象和处理规则进行规范化, 并按照采用统一的接口来编写,而采取规定方法之外的写法都会出错。

    4、创建一个promise对象

    从构造函数 Promise 来创建一个新建新promise对象作为接口。使用new来调用Promise的构造器来进行实例化promise对象。

    var promise = new Promise(function(resolve, reject) {  

      // 异步处理 

     // 处理结束后、调用resolve 或 reject

    });

    5、promise.then() 实例回调方法

    promise.then() 实例promise对象在 resolve(成功) / reject(失败)时调用的回调函数。promise.then(onFulfilled, onRejected)。

    resolve(成功)时onFulfilled 会被调;reject(失败)时onRejected 会被调用;onFulfilled、onRejected 两个都为可选参数。

    promise.then 成功和失败时都可以使用。 如果只想对异常进行处理则可以采用 promise.then(undefined, onRejected) 方式,只指定reject时的回调函数。 不过promise除了then,还有catch方法,捕获失败的处理。 promise.catch(onRejected) 。promise.catch(onRejected)。

    6、简单的例子

    function asyncFunction() {

       return new Promise(function (resolve, reject) {

         setTimeout(function () {

            resolve(value);     

    }, 16);    

    }); 

    }

    asyncFunction().then(function (value) {

     console.log(value);  

      // => value

    }).catch(function (error) {

      console.log(error); 

    });

    asyncFunction 函数返回promise对象, then 方法设置resolve后的回调函数, catch方法设置发生错误时的回调函数。在这种情况下 ,catch 的回调函数不会被执行(因为promise返回了resolve), 如果运行环境没有提供 setTimeout 函数,那么上面代码在执行中就会产生异常,在执行catch 中设置的回调函数。

     * 不使用catch 方法:

    我们可以使用promise.then(onFulfilled, onRejected) 方法声明,只使用 then方法。

    asyncFunction().then(function (value) {

        console.log(value); 

    },function (error) {

      console.log(error); 

    });

    7、实例化的promise对象的状态。

    "has-resolution" - Fulfilled :

    resolve(成功)时。此时会调用 onFulfilled。

    "has-rejection" - Rejected :

    reject(失败)时。此时会调用 onRejected。

    "unresolved" - Pending :

    既不是resolve也不是reject的状态。也就是promise对象刚被创建后的初始化状态等。

    其中 左侧为在ES6 Promises规范中定义的术语, 而右侧则是在Promises/A+中描述状态的术语。

    8、promise对象的状态,从Pending转换为FulfilledRejected之后, 这个promise对象的状态就不会再发生任何变化。在.then 后执行的函数只会被调用一次。FulfilledRejected这两个中的任一状态都可以表示为Settled(不变的)。Settled 代表 resolve(成功) 或 reject(失败)。

    * 关键点:当promise的对象状态发生变化时,用.then 来定义只会被调用一次的函数。

    二、创建promise对象

    1、创建promise对象的流程。

    1.1、实例化promise对象:new Promise(fn) 返回一个promise对象

    1.2、在fn 中指定异步等处理

    处理结果错误的话,调用reject(Error对象)

    处理结果正常的话,调用resolve(处理结果值)

    2、为promise对象添加处理方法

    promise对象的处理方法有两种:

    promise对象被 resolve 时的处理(onFulfilled)

    promise对象被 reject 时的处理(onRejected)

    被resolve后的处理,可以在.then 方法中传入想要调用的函数。(如下面例子)getURL函数中的 resolve(req.responseText); 会将promise对象变为resolve(Fulfilled)状态, 同时使用其值调用 onFulfilled 函数。

    在getURL 的处理中发生任何异常,或者被明确reject的情况下, 该异常原因(Error对象)会作为.catch方法的参数被调用。

    例子:  用Promise来通过异步处理方式来获取XMLHttpRequest(XHR)的数据 :

    声明一个getURL函数,返回一个promise实例包装XHR处理。

    function getURL(URL) {  

      return new Promise(function (resolve, reject) {

       var req = new XMLHttpRequest();

       req.open('GET', URL, true);

     req.onload = function () {

            if (req.status === 200) {         

           resolve(req.responseText);          

    } else {

       reject(new Error(req.statusText));

        }    

        };     

       req.onerror = function () {

        reject(new Error(req.statusText));

       };     

       req.send();  

      }); 

    }

    // 运行示例

    var URL = "http://httpbin.org/get";

    getURL(URL).then(function onFulfilled(value){ // 被resolve后的处理,可以在.then方法中传入想要调用的函数。

      console.log(value);

     }).catch(function onRejected(error){ // 被reject后的处理,可以在.then 的第二个参数或者是在.catch方法中设置想要调用的函数。

      console.error(error);

     });

    getURL 只有在通过XHR取得结果状态为200时才会调用 resolve -其他情况(取得失败)则调用 reject 方法。

    XHR发生错误时 onerror 事件被触发,调用reject。发生错误时需要创建一个Error对象后再将具体的值传进去:reject(new Error(req.statusText)); 。

    3、 Promise.resolve

    new Promise() 方法的快捷方式:静态方法Promise.resolve(value)

    例如:

    基本的用法:

    new Promise(function(resolve){  

      resolve(42);  //resolve(42); 会让promise对象立即进入确定(resolved)状态,并将 value值42 传递给后面then里所指定的 onFulfilled 函数

    });

    语法糖:

    Promise.resolve(42);

    Promise.resolve(value) 方法的返回值是一个promise对象,可以对其返回值进行 .then 调用。

    Promise.resolve(42).then(function(value){   

     console.log(value);

     });

     Promise.resolve 方法的作用就是将传递给它的参数填充(Fulfilled)到promise对象后并返回这个promise对象。

    3.1、Promise.resolve 方法另一个作用就是将thenable对象转换为promise对象

    (thenable指的是一个具有 .then 方法的对象。例如jQuery.ajax())。thenable对象可以使用 Promise.resolve 来转换为一个promise对象。就能直接使用 then 或者 catch 等在ES6 Promises里定义的方法。

    3.1.1、将thenable对象转换promise对象

    var promise = Promise.resolve($.ajax('/json/comment.json'));// => promise对象

    promise.then(function(value){  

     console.log(value);

     });

    *thenable 对象我们一般用不到

    4、 Promise.reject

    Promise.reject(error)是和Promise.resolve(value)类似的静态方法,也是 new Promise() 方法的快捷方式。

    常规写法是:

    new Promise(function(resolve,reject){   

     reject(new Error("error"));

     });      //调用该promise对象通过then指定的 onRejected 函数,并将错误(Error)对象传递给 onRejected 函数。

    语法糖:

    Promise.reject(new Error("error")).catch(function(error){  

      console.error(error); 

    });

    5、promise异步操作

    1、一般的使用情况下,接收回调函数的函数,根据具体的执行情况,可以选择是以同步还是异步的方式对回调函数进行调用。

    异步回调函数同步调用 ?

    NO!!!

    1、绝对不能对异步回调函数(即使在数据已经就绪)进行同步调用。

    2、如果对异步回调函数进行同步调用的话,处理顺序可能会与预期不符,可能带来意料之外的后果。

    3、对异步回调函数进行同步调用,还可能导致栈溢出或异常处理错乱等问题。

    4、如果想在将来某时刻调用异步回调函数的话,可以使用 setTimeout 等异步API。

    例子:

    这个函数会接收一个回调函数进行处理。

    function onReady(fn) {   

     var readyState = document.readyState;

      if (readyState === 'interactive' || readyState === 'complete') {    

        fn();  

    } else {

         window.addEventListener('DOMContentLoaded', fn); 

        }

     }

     onReady(function () {

        console.log('DOM fully loaded and parsed');

     }); 

    console.log('==Starting==');

    如果在调用onReady之前DOM已经载入的话

    如果在调用onReady之前DOM还没有载入的话

    对回调函数进行同步调用

    通过注册 DOMContentLoaded 事件监听器来对回调函数进行异步调用

    因此,如果这段代码在源文件中出现的位置不同,在控制台上打印的log消息顺序也会不同。为了解决这个问题,我们可以选择统一使用异步调用的方式。

    例子2:

    setTimeout 等异步API异步调用回调函数:

    function onReady(fn) {  

      var readyState = document.readyState;

      if (readyState === 'interactive' || readyState === 'complete') {      

    setTimeout(fn, 0);   

    } else {

      window.addEventListener('DOMContentLoaded', fn);  

      } 

    onReady(function () {

        console.log('DOM fully loaded and parsed');

     });

     console.log('==Starting==');

    为了避免上述中同时使用同步、异步调用可能引起的混乱问题,Promise在规范上规定 Promise只能使用异步调用方式 。

    例子三:

    promise重写上述onReady函数:

    function onReadyPromise() {  

      return new Promise(function (resolve, reject) {

      var readyState = document.readyState;

        if (readyState === 'interactive' || readyState === 'complete') {          

      resolve();   

    } else {

           window.addEventListener('DOMContentLoaded', resolve);    

        }  

      }); 

    onReadyPromise().then(function () {

     console.log('DOM fully loaded and parsed');

     }); 

    console.log('==Starting==');

    Promise能保证每次调用都是以异步方式进行。

    6、Promise 方法链method chain

    function taskA() {  

      console.log("Task A"); 

    }

    function taskB() {   

     console.log("Task B"); 

    }

    function onRejected(error) {  

      console.log("Catch Error: A or B", error); 

    }

    function finalTask() {   

     console.log("Final Task"); 

    }

    var promise = Promise.resolve();

     promise

    .then(taskA)

    .then(taskB)

    .catch(onRejected) 

    .then(finalTask);

    执行流程:

    6.1、promise chain 中如何传递参数

    function doubleUp(value) {  

      return value * 2;

     }

    function increment(value) {  

      return value + 1;

     }

    function output(value) {  

      console.log(value);// => (1 + 1) * 2

    }

    var promise = Promise.resolve(1); 

    promise

    .then(increment)

    .then(doubleUp)

    .then(output)

    .catch(function(error){ 

      // promise chain中出现异常的时候会被调用 

    console.error(error);   

     });

    入口函数是 Promise.resolve(1); 

    执行流程:Promise.resolve(1); 传递 1 给 increment 函数;函数 increment 对接收的参数进行 +1 操作并返回结果,接着传给 doubleUp 函数;最后在函数 output 中打印结果。

    * return的值会由 Promise.resolve(return的返回值); 进行相应的包装处理,最终 then 的结果返回一个新创建的promise对象。Promise#then 除了注册一个回调函数,还将回调函数的返回值进行变换,创建并返回一个promise对象。

    特别注意的地方:then 返回返回新创建的promise对象。

    例子:✘ then 的错误使用方法

    function badAsyncCall() {  

      var promise = Promise.resolve();

      promise.then(function() {    

        // 任意处理 

     return newVar;

     });   

     return promise;

     }

    正确方法:then 返回返回新创建的promise对象

    function anAsyncCall() {  

      var promise = Promise.resolve();

      return promise.then(function() {   

         // 任意处理 

       return newVar;

     });

     }

    这种函数的行为贯穿在Promise整体之中,接收一个promise对象为参数,并返回一个和接收参数不同的、新的promise对象。

    7、 使用Promise#then同时处理多个异步请求

    7.1、Promise.all

    Promise.all 接收一个 promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用 .then 方法。

    例子:

    function getURL(URL) {   

     return new Promise(function (resolve, reject) {

          var req = new XMLHttpRequest();

       req.open('GET', URL, true);   

         req.onload = function () {

            if (req.status === 200) {    

                resolve(req.responseText);   

    } else {

                reject(new Error(req.statusText));

              }      

      };       

     req.onerror = function () {

         reject(new Error(req.statusText));

          };  

          req.send();  

      }); 

    }

    var request = {

       comment: function getComment() {       

         return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse);     

       },      

      people: function getPeople() {     

           return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse);    

        }    

    };

    function main() {  

      return Promise.all([request.comment(), request.people()]);

    }

    // 运行示例

    main().then(function (value) {

        console.log(value);

     }).catch(function(error){  

      console.log(error); 

    });

    // request.comment() 和 request.people() 会同时开始执行,而且每个promise的结果(resolve或reject时传递的参数值),和传递给Promise.all的promise数组的顺序一致。

    7.2、Promise.all 在接收到的所有的对象promise都变为 FulFilled 或者 Rejected 状态之后才会继续进行后面的处理。

     与之相对的是 Promise.race 。只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。

    暂时学习到前两章,东西太多,一时间消化不了。

    相关文章

      网友评论

        本文标题:Promise笔记

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