美文网首页程序员
深入解析ES6中的promise

深入解析ES6中的promise

作者: 魔王哪吒 | 来源:发表于2019-11-27 08:42 被阅读0次
    file

    作者 | Jeskson
    来源 | 达达前端小酒馆

    什么是Promise

    Promise对象是用于表示一个异步操作的最终状态(完成或失败)以及其返回的值。

    什么是同步,异步

    同步任务会阻塞程序的执行,如alert,for

    异步任务不会阻塞程序的执行,如setTimeou

    使用Promise,then,catch,finally

    Promise.all 和 Promise.race

    Promise.resolve 和 Promise.reject

    回调与Promise

    回调函数,用于请求数据

    function backFunction(fn) {
     setTimeout(function() {
      fn && fn();
     }, 1000);
    }
    
    // 调用
    backFunction(function() {
     console.log(1); // 1
     backFunction(function() {
      console.log(2); // 2
       backFunction(function() {
        console.log(3); // 3
       });
     });
    });
    

    Promise

    function d() {
     return new Promise(resolve = {
      setTimeout(function() {
       resolve(); // resolve成功的时候要做的事情
      },1000);
      // 1秒后调用resolve(),它是一个函数
     })
    }
    
    d()
     .then(function() {
      console.log(1);
      return d(); // Promise实例
     })
     .then(function() {
      console.log(2);
      return d(); // Promise实例
     }).then(function() {
      console.log(3);
     });
    

    对比回调

    // 动画
    function moveTo(el, x, y, fn) {
     el.style.transform = `translate(${x}px, ${y}px)`;
     setTimeout(function() {
      fn && fn();
     },1000);
    }
    
    let el = document.querySelector('div');
    
    document.querySelector('button').addeventListener('click', e
     moveTo(el, 100, 100, function() {
      console.log(1);
      moveTo(el, 200, 200, function() {
       console.log(2);
      });
     })
    });
    
    // promise
    function moveTo(el,x,y) {
     return new Promise(resolve => {
      el.style.transform = `translate(${x}px, ${y}px)`;
      setTimeout(function() {
       resolve();
      },1000);
     });
    }
    
    document.querySelector('button').addEventListener('click', e=>{
     moveTo(el,100,100)
      .then(function() {
       console.log('da');
       return moveTo(el, 200, 200);
      })
      .then(function() {
        console.log('da2');
      }).then(function() {
        console.log('da2');
      });
    });
    

    信任问题

    // 使用第三方库 回调
    function method(fn) {
     // 回调
     setTimeout(function() {
      // 回调
      fn && fn();
      // 有可以有bug,被多调用一次
      fn && fn();
     },1000);
    }
    
    // promise一旦被调用,成功或者是失败后,就不能再被修改
    
    function method() {
     return new Promise(resolve => {
      setTimeout(function() {
       //成功
       resolve();
       // 再调用就不会执行
       resolve();
      },1000);
     });
    }
    
    // 控制反转
    
    function method(fn) {
     setTimeout(function() {
      // 执行回调
      fn && fn.call({a:1, b:2)};
      // 改变指向
     },1000);
    }
    
    function method(fn) {
     return new Promise(resolve => {
      setTimeout(() => {
       resolve();
      },1000);
     });
    }
    

    错误处理

    then(resolve, reject)
    then方法中的第二个回调,是失败的时候要做的事情

    catch
    使用实例的then方法,可以捕获错误

    finally
    不论成功与否,finally中的内容一定会执行

    function fn(val) {
     return new Promise((resolve, reject) => {
      if(val) {
       resolve(); // 成功的时候
      } else {
       reject(); // 失败的时候
      }
     });
    }
    
    fn(false)
     .then( () => {
      console.log("成功");
     }, () => {
      console.log("失败");
     })
    
    function fn(val) {
     return new Promise((resolve, reject) => {
      if(val) {
       resolve(); // 成功的时候
      } else {
       reject('404'); // 失败的时候
      }
     });
    }
    
    fn(false)
     .then( () => {
      console.log("成功");
     }, e => {
      console.log(e);
     })
    
     promise 中resolve只能传递一个参数,如下:
     
     function fn(val) {
     return new Promise((resolve, reject) => {
      if(val) {
       resolve({name: 'da'}); // 成功的时候
      } else {
       reject('404'); // 失败的时候
      }
     });
    }
    
    fn(true)
     .then( dada => {
      console.log(data);
     }, e => {
      console.log(e);
     })
    

    catch会捕获错误,如果在回调中没有对错误进行处理

    fn(true)
     .then(data => {
      console.log(data);
      return fn(false);
     })
     .then( () => {
      console.log('da'); // 不会执行,没处理错误
     })
     .then( () => {
      
     })
     .catch(e => {
      console.log(e);
      return fn(false);
     }); // 直接输出到这
     
     不能保证catch被执行
    

    如果没有对失败做出处理,会报错

    fn(true) 
     .then(data => {
      console.log(data);
      return fn(false);
     })
     .catch(e=> {
      // 捕获错误
      console.log(e);
      return fn(false);
     })
     .finally( () => {
      console.log(100);
     });
    

    Promise的三种状态

    pending为进行中的状态,fulfilled为成功的状态,rejected为失败的状态。状态的改变时不可返的,一旦决议就不能修改(决议,状态的改变为决议),状态只能从pending到fulfilled,或者,从pending到rejected。

    Promise.all方法可以把多个promise的实例包装成一个新的promise实例

    Promise.all( [promise1, promise2] ) : Promise
    
    数组中,如果promise都为true,则返回为true,决议为成功
    如果数组中有一个为promise,那么返回的是false,决议为失败
    
    如果是一个空数组,那么返回为true,决议为成功
    

    模式多个请求的数据

    function getData1() {
     return new Promise((resolve, reject) => {
      setTimeout( () => {
       console.log('第一条数据加载成功');
       resolve('data1');
      },1000);
     });
    }
    
    function getData2() {
     return new Promise((resolve, reject) => {
      setTimeout( () => {
       console.log('第二条数据加载成功');
       resolve('data2');
      },1000);
     });
    }
    
    function getData3() {
     return new Promise((resolve, reject) => {
      setTimeout( () => {
       console.log('第三条数据加载成功');
       resolve('data3'); // 改为 reject('err')
      },1000);
     });
    }
    
    let p = Promise.all( [getData1(), getData2(), getData3()] );
    
    p.then(arr => {
     console.log(arr);
    });
    
    // 失败
    
    p.then(arr => {
     console.log(arr);
    }, e => {
     console.log(e);
    });
    
    let p = Promise.all([]); // 决议为成功
    
    p.then( () => {
     console.log(`da`);
    }, e => {
     console.log(e);
    });
    
    第一条数据加载成功
    第二条数据加载成功
    第三条数据加载成功
    

    不用Promise.all

    let count = 0;
    function getData1() {
     setTimeout( () => {
      console.log('第一条数据加载成功');
      count ++;
      func();
      },1000);
    }
    
    function getData2() {
     setTimeout( () => {
      console.log('第二条数据加载成功');
      count ++;
      func();
      },1000);
    }
    
    function getData3() {
     setTimeout( () => {
      console.log('第三条数据加载成功');
      count ++;
      func();
      },1000);
    }
    
    function getData4() {
     setTimeout( () => {
      console.log('第四条数据加载成功');
      count ++;
      func();
      },1000);
    }
    
    // 写一个方法:
    function func() {
     if(count < 4)return;
     console.log('全部拿到了');
    }
    

    调用

    getData2();
    getData3();
    getData4();
    
    file
    let err = false;
    function getData1() {
     setTimeout( () => {
      console.log('第一条数据加载成功');
      if(status) err = true;
      count ++;
      func();
      },1000);
    }
    
    function func() {
     if(count < 4)return;
     console.log('全部拿到了');
    
     if(err) {
      // ...
     }
    }
    

    Promise.race()

    Promise.race([promise1, promise2]) : Promise
    
    let p = Promise.race([getData1(), getData2(),getData3()]);
    
    p.then (data=>{
     console.log(data);
    })
    
    let flag = false;
    function func(data) {
     if(flag) return
     flag = true;
     console.log(data);
    }
    
    function getData1() {
     setTimeout(()=>{
      console.log("第一条数据加载成功");
      func({name: 'da'});
     },500);
    }
    
    function getData2() {
     setTimeout( () => {
      console.log("第二条数据加载成功");
      func({name: 'dada'});
     }, 1000);
    }
    
    getData1();
    getData2();
    
    第一条数据加载成功
    {name: 'da'}
    第二条数据加载成功
    

    Promise.resolve与Promise.reject

    Promise.resolve() 与 Promise.reject()

    // Promise.resolve
    传递一个普通的值
    let p1 = new Promise(resolve => {
     resolve('成功!');
    });
    
    let p2 = Promise.resolve('成功!');
    
    // 传递一个promise实例
    let pro = new Promise(resolve => {
     resolve('da');
    });
    
    let p = Promise.resolve(pro);
    
    p.then(data => void console.log(data));
    
    let obj = {
     then (cb) {
      console.log('da');
      da('dada');
     },
     method() {
      console.log('coding');
     }
    }
    
    // 立即执行
    Promise.resolve(obj).then(data => {
     console.log(data);
    });
    

    Promise异步:

    console.log(1);
    
    let p = new Promise(resolve => {
     console.log(2);
     resolve();
     console.log(3);
    });
    
    console.log(4);
    
    p.then(()=>{
     console.log(5);
    });
    
    console.log(6);
    
    // 123465
    
    

    Promise改善了传统回调造成的代码难维护,控制反转等问题,promise是异步的,如果all接收的是空数组,马上会被决议为成功,如果race接受的是空数组,那么会被永远挂起,无限捕获错误问题。

    resove和reject方法:

    如果接收的是普通的值,那么就会立即决议为成功,并填充这个值,如果接收的是一个promise实例,那么返回这个promise实例,如果接收的是个thenable对象,则会把它包装成promise对象,并立即执行这个对象的then方法,reject会产生一个决议失败的promise并直接传递值。

    JavaScript/ES6 Promise

    JavaScript的Promise代表一个操作的结果还没有结果,就是如网络请求操作,当我们从某个数据源获取数据的时候,没有办法确定它什么时候能够返回,接受到响应。

    Promise提供标准

    doSomething()
     .then(doSomethingElse)
     .catch(handleError)
     .then(doMore)
     .then(doFinally)
     .catch(handleAnotherError)
    

    创建Promise

    一个Promise使用Promise构造器创建,接受两个参数resolve,reject

    
    var promise = new Promise( function(resolve, reject) {
     // new Promise resolve() reject()
    }
    
    
    XMLHttpRequest的promise版本:
    
    function get(url) {
     return new Promise(function(resolve, reject) {
      var req = new XMLHttpRequest();
      req.open('GET', url);
      req.onload = function() {
       if(req.status == 200) {
        resolve(req.response);
       }else{
        reject(Error(req.statusText));
       }
      };
      req.onerror = function() {
       reject(Error("Network Error"));
      };
       req.send();
      });
     }
    

    使用Promise

    get(url)
     .then(function(response) {
     },function(err) {
     })
    

    处理错误:

    get(url)
     .then(function(response){
     }, undefined)
     .then(undefined, function(err){
     })
    
    get(url)
     .then(function(response){
     })
     .catch(function(err){
     })
    

    链式

    get(url)
     .then(function(response){
      response = JSON.parse(response);
      var secondURL = response.data.url
      return get(secondURL);
     })
     .then(function(response){
      response = JSON.parse(response);
      var thirdURL = response.data.url
      return get(thirdURL);
     })
     .catch(function(err){
      handleError(err);
     });
    

    并行执行Promise

    Promise.all()方法每个promise数组成为则决议为成功,如果数组中有任意一个promise为失败则决议为失败。

    任务一,任务二,任务三,.then() -> success 任务成功

    ES6

    Promise对象用于表示一个异步操作的最终状态,以及其返回的值。

    语法:

    new Promise(function (resolve, reject) {
    });
    

    几种状态:

    pending初始状态,既不是成功,也不是失败状态;fulfilled意味着操作成功完成;rejected意味着操作失败。

    pending状态的Promise对象可能会触发filfilled状态,并传递一个值给响应的状态处理方法,也可能触发失败状态rejected并传递失败信息。

    Promise.all(iterable)

    这个方法返回一个新的promise对象,该promise对象在itearable参数中,当里面所有的的promise对象决议成功的时候才触发成功,否则里面如何一个promise对象决议失败的时候,立即触发promise对象的失败。

    Promise.all方法常用于处理多个promise对象的状态集合。

    Promise.race(iterable)

    当iterable参数里的任意一个子promise被决议成功或者是决议失败后,父promise会用子promise的成功返回值,或是失败返回。

    Promise.reject(reason)

    返回一个状态为失败的promise对象,将给定的失败信息传递给对应的处理方法。

    Promise.resolve(value)

    返回一个状态为失败的promise对象,将给定的失败信息传递给对应的处理方法。

    const myPromise = new Promise( (resolve, reject) => {
     resolve('resolve'); // filfilled
     reject('reject'); // rejected
    });
    
    function myFunction(url) {
     return new Promise( (resolve, reject) => {
      xhr.open ("GET", url);
      xhr.onload = () => resolve(xhr.responseText);
      xhr.onerror = () => reject(xhr.statusText);
      xhr.send();
     });
    };
    

    当异步代码执行成功的时候,会调用resolve(),当异步代码执行失败的时候,会调用reject()。

    模拟异步代码:

    setTimeout(function(){
     resolve('成功');
    },250);
    });
    
    myPromise.then(function(successMsg){
    });
    

    ES6 Promise对象

    Promise对象是异步编程的一种解决方案,语法上,Promise是一个对象,从它那可以获取异步操作的信息。

    Promise的状态,promise异步操作有三种状态,pending(进行中),fulfilled(已成功),reject(已失败)。除了异步操作的结果,任何其他操作都是无法改变这个状态。

    const p1 = new Promise(function(resolve,reject){
        resolve('success1');
        resolve('success2');
    }); 
    
    const p2 = new Promise(function(resolve,reject){  
        resolve('success3'); 
        reject('reject');
    });
    
    p1.then(function(value){  
        console.log(value); // success1
    });
    
    p2.then(function(value){ 
        console.log(value); // success3
    });
    

    缺点,一旦建立Promise就会立即执行,无法中途取消,如果不设置回调函数,Promise内部会抛出错误,不会反应到外部。

    then方法,接收两个函数作为参数。

    第一个参数是 Promise 执行成功时的回调,第二个参数是 Promise 执行失败时的回调,两个函数只会有一个被调用。

    const p = new Promise(function(resolve,reject){
      resolve('success');
    });
     
    p.then(function(value){
      console.log(value);
    });
     
    console.log('first');
    // first
    // success
    
    const p = new Promise(function(resolve,reject){
      resolve(1);
    }).then(function(value){ // 第一个then // 1
      console.log(value);
      return value * 2;
    }).then(function(value){ // 第二个then // 2
      console.log(value);
    }).then(function(value){ // 第三个then // undefined
      console.log(value);
      return Promise.resolve('resolve'); 
    }).then(function(value){ // 第四个then // resolve
      console.log(value);
      return Promise.reject('reject'); 
    }).then(function(value){ // 第五个then //reject:reject
      console.log('resolve:' + value);
    }, function(err) {
      console.log('reject:' + err);
    });
    
    

    then方法将返回一个resolved或是rejected状态的Promise对象用于链式调用。

    热Promise

    在JavaScript中,所有代码都是单线程的,也就是同步执行的,promise就是为了提供一个解决方案的异步编程。

    promise的特点:只有异步操作可以决定当前处于的状态,并且任何其他操作无法改变这个状态;一旦状态改变,就不会在变。

    状态改变的过程:从pending变为fulfilled和从pending变为rejected,状态改变后,就不会在改变了,这就叫已定型resolved

    用法:

    Promise对象是由关键字new及其构造函数来创建的。

    
    const promise = new Promise((resolve, reject) => {
        // do something here ...
        if (success) {
            resolve(value); // fulfilled
        } else {
            reject(error); // rejected
        }
    }); 
    

    函数接收两个函数作为参数,分别是resolve和reject,当异步操作执行成功后,会将异步操作的结果作为参数传入resolve函数并执行,此时的状态由Promise状态从pending变为fulfilled;而失败会将异步操作的错误作为参数传入reject函数并执行,此时Promise对象状态从pending变为rejected。

    通过then方法,将指定resolved状态和rejected状态的回调函数。

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

    Promise.all(iterable),iterable必须是一个可以迭代的对象,如Array

    返回值为一个新的Promise实例。

    var p1 = new Promise((resolve, reject) => { 
      setTimeout(resolve, 1000, 'one'); 
    }); 
    var p2 = new Promise((resolve, reject) => { 
      setTimeout(resolve, 2000, 'two'); 
    });
    var p3 = new Promise((resolve, reject) => {
      setTimeout(resolve, 3000, 'three');
    });
    var p4 = new Promise((resolve, reject) => {
      reject('p4 reject!');
    });
    var p5 = new Promise((resolve, reject) => {
      reject('p5 reject!');
    });
    
    Promise.all([p1, p2, p3, p4, p5]).then(values => { 
      console.log(values);
    }, reason => {
      console.log(reason)
    });
    
    // p4 reject!
    

    Promise.race(iterable),同理,返回值为一个新的Promise实例。

    返回的新实例状态,会是最先改变状态的那个实例,如果不是Promise实例,先用Promise.resolve方法,如果传入的迭代为空,则返回的Promise永久等待。

    一个Promise实例
    原封不动的返回该实例;

    var original = Promise.resolve('第二行');
    
    var da = Promise.resolve(original);
    
    da.then(function(value) {
      console.log('value: ' + value);
    });
    
    console.log('original === da ? ' + (original === da));
    
    // "original === da ? true"
    // "value: 第二行"
    

    跟随这个thenable对象的,采用它的最终状态;

    let thenable = {
      then: function(resolve, reject) {
        resolve(41);
      }
    }
    
    let p = Promise.resolve(thenable);
    
    p.then(function(value) {
      console.log(value);
    })
    
     // 41
    

    直接将传入参数当最终结果,并返回一个新的Promise;

    let p = Promsie.resolve(12);
    
    p.then(function(number) {
      console.log(number);
    })
    
    // 12
    

    直接返回一个resolved状态的Promise对象

    let p = Promsie.resovle();
    
    p.then(function() {
      // do something 
    })
    

    Promise.prototype.then()

    p.then(onResolve, onReject);
    
    p.then(function(value) {
      }, function(reason) {
    });
    

    Promise.prototype. catch()

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

    Promise.prototype. finally()

      p.finally(onFinally);
    
      p.finally(function() {
       
      })
    

    该回调函数的不接受任何参数

    promise是一个对象,代表一个异步操作,有三种状态,进行中,成功,失败。只有异步操作的结果的可以决定当前是哪种状态,promise一旦新建执行,就没有办法中途停止。

    Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。只有当作为参数所有的promise函数运行完毕,才会执行.then回调。

    file file file
    //以往回调方式
    函数1(function(){
        //代码执行...(ajax1)
        
        函数2(function(){
            //代码执行...(ajax2)
            
            函数3(function(data3){
                  //代码执行...(ajax3)
            });
            ...
        });
    });
    
    //Promise回调方式:链式调用,可构建多个回调函数。
    promise().then().then()...catch()
    
    
    //创建Promise实例
    let promise = new Promise( (resolve, reject) => {
        //执行相应代码 根据情况调用resolve或reject
        ...
    })
    
    //promise的then方法中执行回调
    promise.then(function(){
            //第一个参数是返回resolve时
        },function(){
            //第二个是回调返回reject时
        }
    }
    

    定时器调用

    const promise = new Promise(function(resolve, reject){
        setTimeout(resolve,1000);
    })
    
    promise.then(function(){
      console.log('resolve:成功回调函数')  
    },function(){
      console.log('reject:失败回调函数')
    })
    
    file

    传递参数:

    const promise = new Promise((resolve, reject) => {
        setTimeout(reject,1000,'我是value值');
    })
    
    promise.then((value) => {
        console.log('resolve:' + value)
    }).catch((value) => {
        console.log('reject:'+ value)
    })
    
    //第一种,单个传值是无效的
    const promise = new Promise((resolve, reject) => {
        setTimeout(resolve,1000,'参数1','参数2');
    })
    
    promise.then((value1,value2) => {
        console.log('value1:' + value1)     //value1:参数1
        console.log('value2:' + value2)     //value2:undefined
    }).catch((value) => {
        console.log(value)
    })
    
    //第二种:数组传值
    const promise = new Promise((resolve, reject) => {
        setTimeout(resolve,1000,['参数1','参数2']);
    })
    promise.then((value1) => {
        console.log('value1:' + value1)     //value1:参数1,参数2
    }).catch((value) => {
        console.log(value)
    })
    
    

    Promise.prototype.then方法:链式操作

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

    Promise.prototype.catch方法:捕捉错误

    getJSON("/posts.json").then(function(posts) {
    }).catch(function(error) {
      console.log('发生错误!', error);
    });
    

    Promise.all方法,Promise.race方法

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

    ❤️ 不要忘记留下你学习的脚印 [点赞 收藏 评论]

    作者Info:

    【作者】:Jeskson

    【原创公众号】:达达前端小酒馆。

    【转载说明】:转载请说明出处,谢谢合作!~

    关于目前文章内容即涉及前端,PHP知识点,如果有兴趣即可关注,很荣幸,能被您发现,真是慧眼识英!也感谢您的关注,在未来的日子里,希望能够一直默默的支持我,我也会努力写出更多优秀的作品。我们一起成长,从零基础学编程,将 Web前端领域、数据结构与算法、网络原理等通俗易懂的呈现给小伙伴。分享 Web 前端相关的技术文章、工具资源、精选课程、热点资讯。


    若本号内容有做得不到位的地方(比如:涉及版权或其他问题),请及时联系我们进行整改即可,会在第一时间进行处理。


    请点赞!因为你们的赞同/鼓励是我写作的最大动力!

    欢迎关注达达的CSDN!

    这是一个有质量,有态度的博客

    7d927f18ebd05ea1d505a572393fbc87.jpg

    相关文章

      网友评论

        本文标题:深入解析ES6中的promise

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