Promise in Javascript

作者: 细密画红 | 来源:发表于2017-06-15 17:38 被阅读56次

    promise you have a good time here !

    The Promise object is used for deferred and asynchronous computations.
    简单来说,promise 是一个用来处理延迟和异步的对象。

    故事要从回调开始说起

    callback(回调) 是 javascript 默认的用来处理异步请求的方法。

    通常我们将一个函数 A 当做另一个函数 B 的参数传入,在未来满足某个条件的情况下,函数 B 会调用 A 。例如:

    function loadImg(src,parent,callback){
          var img=document.createElement('img');
          img.src=src;
          img.onload=callback; // 图片加载完成后调用回调函数
          parent.appendChild(img);
    } 
    

    回调的代码是可以正常工作的。那么它会有什么问题呢?

    如果这里的callback也需要一个回调参数呢?然后这个回调参数又需要另一个回调?你知道这个问题是可以无限进行下去的吧。。。于是就有了这样的代码,传说中的 Pyramid of Doom ! 金字塔式的代码调用。

    image.png

    这里问题就很明显了

    • 错误处理:每一个回调都要单独进行错误处理。
    • 调试困难

    那么如果我们的代码可以写成这样呢:


    image.png

    Promise

    4个阶段:stages of promise
    • Pending : waiting.一般初始化一个promise对象时的状态就是pending,这个时候还没有成功或者失败。
    • Fulfilled (resolve) : It worked ! 相关的方法成功啦!
    • **Rejected **: It didn't work ! 相关的方法失败啦!
    • **Settled **: Someting happend ! 方法处理完了,它要么成功了,要么失败了。
    包裹 :wrapping

    **promise is a try-catch wrapper around code that will finished at an unpredictable time. **

    异步代码的特点就是我们不知道它什么时候结束。promise 就是对这类代码的一层包装而已,nothing more. 这样包装的好处就是不用再像之前的回调地狱那样来写代码。

    那么 promise 的代码怎么写呢:coding

    promise是一个构造函数

    image.png
    • 任何传递给resolve或者reject的参数都会作为参数被传给下一个then(或者catch)中的处理方法。
    • 如果没有传递任何参数,那么接收到的就是undefined.
    • 如果resolve方法里的参数也是一个promise,那么这个promise会先被执行,然后它的resolve里的参数会被传给下一个then. 注意,resolve后会执行链上的下一个then, reject会执行链上的下一个catch.

    小练习1:包装一个图片请求

    function loadImg(src,parent,callback){
          var img=document.createElement('img');
          img.src=src;
          img.onload=callback; // 图片加载完成后调用回调函数
          parent.appendChild(img);
    } 
    

    对这段代码用promise包装一下,如下:

    new Promise(function(resolve,reject){
       var img=document.createElement('img');
       img.src='image.jpg';
       img.onload=resolve,
       img.onerror=reject;
       document.body.appenndChild(img);
    })
     .then(finishLoading)
     .catch(showAlternateImage);
    
    

    一旦 resolve 或者 reject 被调用了,就说明这个 promise 已经被处理的(settled)了,然后方法链上的下一个then(或者catch)开始被调用。

    小练习2:包装一个setTimeout

    <script>
      function wait(ms){
       /*
        要求:
        1. 用 promise 来保证 setTimeout 方法,在 setTimeout的回调里调用resolve()
        2.注意要让wait方法返回这个promise
       */
        window.setTimeout(function(){},ms);
     }
    </script>
    

    答案:

    function wait(ms){
      return new Promise(function(resolve){
         setTimeout(function(){
           resolve('hello world');
        },ms);
      });
    }
    wait(300).then(function(data){
      console.log("this is "+data);
    });
    
    

    小练习3:包装一个xhr

    function get(url){
      var req=new XMLHttpRequest();
       req.open('GET',url);
       req.onload=function(){
         if(req.status==200){
           // 请求成功,可以把返回数据res.response传给resolve方法中
         }else{
           // 请求失败,可以把失败信息 req.statusText传给reject中
         };
       };
      req.onerror=function(){
         //请求失败,把"Network Error" 传给reject
      };
      req.send();
    }
    

    答案:

    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(req.statusText);
           }
         };
         req.onerror=function(){
           reject(Error("Network Error"));
       };
       req.send();
     });
    }
    //调用
    get("http://www.test.com/")
    .then(function(res){
         console.log(res);
    })
    .catch(function(res){
        //handle the error
    });
    
    

    Fetch API

    前面的 xhr对象 和 promise 的包装代码是不是很烦人?获取数据不应该这么麻烦。chromn里自带的 fetch API 可以让我们省去很多 xhr 对象的麻烦设置,而且,it is built on native promise !

    Fetch API 用法:

    fetch('https://www.test.com/',{
      method:'get'
    }).then(function(response){
    
    }).catch(function(error){
     
    });
    
    

    记得上一章写的那个很长的 get(url) 吗?额。。。其实它只要这么写就可以了...

    function get(url){
       return fetch(url,{
        method:'get'
      }
    }
    

    thenable

    then方法返回的是一个 promise 对象,我们可以在一个初始方法(一般返回promise对象)上调用后连接 then方法,也可以在 then 方法后面连接 then 方法,因为它自身返回的也是 promise对象。开发者一般用 thenable 这个词来描述 promise 对象和 .thens.

    any method or object that returns .then is thenable.
    anything thenable can become a part of chain of asynchronous work.

    当创建一个异步调用链时,链上的每个方法接收到的,要么是前面的 promise 成功时传递过来的参数,或者是前一个 then 方法返回的值。这样你就可以在异步的方法之间传递参数。

    be able to chain thenables 对于简化异步代码的顺序调用非常有用。

    Error Handling Strategies 错误处理策略

    image.png

    注意,这两种错误处理是一样的。.catch() 方法 其实是 .then ( undefined , rejectFunc ) 的缩写而已。例如,
    get('example.json').then(resolveFunc,rejectFunc) ,如果get方法里有错误的话,rejectFunc就会被调用。

    总的来说,一旦promise 是rejected的,javascript引擎就会跳转到调用链上的下一个rejectJFunc,无论这个方法是写在catch里还是then里。

    但还是建议使用catch方法,因为可读性强一些,并且写起来方便一些。

    注意,.catch 和第二个回调的rejectFunc在执行的顺序会有不同。如下所示:


    image.png

    上面的代码块,如果resolveFunc出错了,那么下面catch里的这个rejectFunc就会被执行。这两个方法是可以同时被执行的。但如果用下面代码行的写法,同一个 then 里的两个方法,只有一个会被执行,或者两个都不执行,如果这里resolveFunc出错了,那么javascript引擎会尝试寻找 then 之后的链上的rejectFunc方法。

    chaining stage : chaining promises together

    在处理多个异步请求时有两种实现方式

    • 顺序 series
    • 并行 Parallel

    同步代码总是按顺序执行的,但是异步代码可以顺序,也可以并行执行。

    看看下面这段代码有什么问题?

    image.png

    answer: 我们无法预测请求返回的顺序,所以我们不知道哪一个getJson promise会先resolve, 就是说他们resovle 的顺序和他们被创建的顺序可能会不一样。这样的话,thumbnails方法的创建就会是随机的。当然,这并不是一个错误。但它引出了一个问题,我们怎么样才能按我们所期望的顺序来创建这些thumbnails方法?

    创建一个按顺序执行的promise串(利用数组方法,不再手工创建promise 串)
    思路:

    image.png

    解决方法一:

    image.png

    但是这样做有问题吗?
    good news: thumbnails按顺序执行
    bad news: 同步执行,时间叠加

    image.png

    解决方法二:

    image.png

    执行时间:

    image.png

    问题:不能保证请求顺序

    解决方法三:不用sequence,使用map

    image.png
    Promise.all
    image.png

    相关文章

      网友评论

        本文标题:Promise in Javascript

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