Promise 对象 适用vue-resource,axios

Promise 对象

  1. Promise 的含义

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

    所谓Promise, 简单是就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作) 的结果。 从语法上来讲,Promise是一个对象,从它可以获取异步操作的信息。Promise 提供了统一的API,各种异步操作都可以用同样的方法进行处理。

    promise的两个特点

    ​ (1): 对象的状态不受外界的影响,Promise对象代表一个异步操作,有三种状态: pending(进行中)、fulfilled(已成功) 和 rejected(已失败)。 只有异步操作的结果,可以决定当前是哪一种状态。任何其他操作都无法改变这个状态。 这也是Promise 这个名字的由来。

    ​ (2) 一旦状态确定,就不会再变,任何时候都可以得到这个结果。Promise对象状态改变,只有两种可能,从pending 变为 fulfilled 和 从pending 变为 rejected 。 只要这两种情况发生,状态就凝固了, 不会再次改变,会一直保持这个结果,这时就称为resolved(已定型)。 如果改变已经发生了, 你再对Promise对象添加回调函数,也会立即监听到这个结果。这与事件(Event) 完全不同 , 事件的特点是, 如果你错过了它,再去监听, 是得不到结果的

    ​ (3) 有了Promise对象,就可以将异步操作以同步操作的流程表达出来, 避免了层层嵌套的回调函数,此外,Promise 对象提供了统一的接口,使得控制异步操作更加容器。

    ​ (4)Promise 也有一些缺点, 首先,无法取消Promise , 一旦新建它就会立即执行, 无法中途取消, 其次,如果不设置回调函数 ,Promise 内部抛出错误,会直接抛出到控制台,没有方法可以兜底。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(无法确定是刚刚开始 还是即将完成)。

    function test(resolve , reject){
    	var timeOut = Math.random() * 2;
    	console.log("设置时间为" + timeOut + "秒");
    	setTimeout(function(){
    		if(timeOut < 1){
    			console.log("执行成功");
    			resolve('200 OK');
    		}else{
    			console.log("执行失败");
    			reject("500 error");
    		}
    	},timeOut * 1000)
    }
    var p1 = new Promise(test);
    var p2 = p1.then(function(result){
    	console.log('成功信息 ' + result);
    })
    var p3 = p2.catch(function(a){
    	console.log('错误信息' , a);
    }) 
    new Promise(test).then(() => console.log("执行成功")).catch(() => console.log("执行失败"))
    
  2. 基本用法

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

    const promise = new Promise(function(resolve, reject) {
      // ... some code
    
      if (/* 异步操作成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    });
    

    ​ Promise 构造函数接受 一个函数作为参数,该函数的两个参数分别是resolve 和 reject 。 他们是两个函数, 由JavaScript引擎提供, 不用自己去实现写法!

    ​ resolve 函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved), 在异步操作成功时调用, 并将异步操作的结果,作为参数传递出去;reject 函数的作用是,将promise对象状态从“未完成”变为“失败”(即从pending变为rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

    ​ Promise实例生成以后,可以用then方法分别指出resolved状态和rejected状态的回调函数。

    接下来看一个例子!

    ```javascript function timeout(ms){ return new Promise((resolve , reject) => { // setTimeout(// 要执行的函数,毫秒数 ,执行函数所需要的参数) setTimeout(resolve , ms , 'done'); }); } timeout(2000).then((value) =>{ console.log(value); })
    
    上面的代码中, timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果,过了指定的时间(ms)以后,Promise实例状态变为resolved , 就会触发then方法绑定回调函数。
    
    <h3>通常情况下,Promise 再新建后会被 <span style='color:red'>立即执行</span> </h3>
    ```javascript
    let promise = new Promise(function(resolve , reject){
    	console.log("Promise 正在执行..."); 
    	// 返回了成功的内容
    	resolve();
    })
    promise.then(function(){
    	console.log("拿到返回值....");
    });
    console.log('你好啊!');
    

    上面的代码中 ,Promise 新建后立即执行,所以首先输出的是Promise 。然后then方法指定了回调函数 ,将在当前脚本所有的同步任务执行完成后才会执行,所以resolved最后输出。

    接下来,用Promise 完成一次网络请求

    ```javascript 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)); }; // 设置图片的sc image.src = url; }); }

    loadImageAsync(‘https://cn.bing.com/th?id=OHR.RainierClouds_ENUS_SS1021697089_1920x1080_HD_ZH-CN170801398.jpg&rf=LaDigue_1366x768.jpg&pid=hp’).then(function (value){
    console.log(‘成功’)
    var body = document.getElementsByTagName(“body”)[0];
    console.log(body);
    body.appendChild(value);
    console.log(value);
    }).catch(function (value){
    console.log("失败 ");
    console.log(value);
    })

    
    上面的代码中,使用Promise 包装了一个图片加载的异步操作 , 如果加载成功 , 就调用resolve方法,否则就调用reject 方法。
    
    <h4>接下来,再来一个异步调用的例子</h4>
    ```javascript
    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.statusTest));
    			}
    		};
    		const client = new XMLHttpRequest();
    		client.open("GET",url);
    		client.onreadystatechange = handler;
    		client.responseType = 'json';
    		client.setRequestHeader('Accept',"application/json");
    		client.send();
    	});
    	return promise;
    }
    getJson("http://182.254.147.230/auth/api/ckusr?name=itrip%40163.com").then(function(json){
    	console.log(json);
    }).catch(function(error){
    	console.log(error);
    })
    

    上面代码中 , getJson是XMLHttpRequest 对象的封装,用于发出一个针对JSON数据的Http请求,并且返回一个Promise对象,需要注意的是, 在getJSON内部,resolve 函数和reject 函数调用是都带有函数

    如果调用resolve函数和reject函数带有参数, 那么他们的参数会被传递给回调函数。reject函数的的参数通常是Error对象的实例,表示抛出的错误;resolve函数除了正常的值之外,还可能是另一个Promise 实例。

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

    上面的代码中,p1 和 p2 都是Promise的实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个几步操作。

    注意,这是p1 的状态就会传递给p2 也就是说,p1的状态决定了p2的状态,如果p1的状态是pending那么p2的回调函数就会等待p1的状态做出改变;如果p1的状态是resolved 或者 rejected 那么p2 的回调函数将会立即执行。

    const p1 = new Promise(function (resolve, reject) {
    	console.log("p1 执行")
      setTimeout(() => resolve(new Error('fail')), 1000)
    })
    
    const p2 = new Promise(function (resolve, reject) {
    	console.log('p2执行');
      setTimeout(() => resolve(p1), 1000)
    })
    
    p2
      .then(result => console.log("success",result))
      .catch(error => console.log(error))
    

    上面代码的整体意思是,p2 怎么走完全是看p1的脸色来。

    注意 调用resolve或者reject并不会终结Promise的参数函数的执行

    new Promise((resolve, reject) => {
      resolve(1);
      console.log(2);
    }).then(r => {
      console.log(r);
    });
    // 2
    // 1
    

    上面的代码中,调用resolve(1) 以后。 后面的console.log(2) 还是会执行的,并且会首先打印出来, 这是因为立即resolved的Promise是在本轮事件循环末尾执行,总是晚于本轮的循环的同步认任务。

    通常情况来讲, 调用resolve 或 reject 以后。promise的使命就完成了。后继操作应该放到then方法里面,而不应该直接写在resolve 或 reject 的后面, 所以,最好在他们前面加上return 语句。这样就可以确保没有意外

    Promise.prototype .then

    primise实例具有then 方法,也就是说,then方法是定义在原型对象上的, 它的作用是为promise 实例添加状态改变时的回调函数,前面说过,then 方法的第一个参数时resolved状态的回调函数,第二个参数是rejected的回调将函数,then 方法返回的一个新拿到Promise实例,(注意是一个新的实例,不是原来的)。因此可以采用链式写法,即then后面可以再调用另一个then方法 。

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

    上面的代码使用then方法,依次指定了两个回调函数,第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。

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

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

    上面的代码中,第一个then方法指定的回调函数,返回的是是另一个Promise对象,这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。并调用相应的参数。

    Promise.prototype.catch()

    promise.prototype.catch 方法是,then(null , rejection) 或.then(undefined,rejection )的别名,用于指定发生错误时的回调函数,如果promise 中发生了错误,会被catch()捕获 ,

    在promise对象中reject方法类似于抛出异常

    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
    

    上面代码中,Promise 在resolve语句后面, 再抛出错误,不会被捕获。等于没有抛出,因为Promise的状态一旦改变,就永久保持,不会再变了。

    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()产生。它们只中任何一个抛出的错误都会被chtch()捕获;

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

    通常来讲,不建议第一种写法, 理由是第二种写法可以捕获前then方法 执行中的错误, 也更将近同步的写法(try/catch)建议总是使用chatch方法。而不使用then方法的第二个参数。

    跟传统的try/catch代码不同的是, 如果没有使用catch() 方法指定错误的处理的回调函数,Promise对象抛出的错误不会传递到外层代码,既不会有任何反应。

    const someAsyncThing = function() {
      return new Promise(function(resolve, reject) {
        // 下面一行会报错,因为x没有声明
        resolve(x + 2);
      });
    };
    
    someAsyncThing().then(function() {
      console.log('everything is great');
    });
    
    setTimeout(() => { console.log(123) }, 2000);
    // Uncaught (in promise) ReferenceError: x is not defined
    // 123
    

    上面代码中, 函数产生了Promise 对象, 内部有语法错误, 浏览器运行到这一行,会报错,但是不会退出进程,终止脚本(通常js中如果报错会直接中止代码的运行)。2秒之后会还是会输出123 ,这就是说,Promise 内部的错误不会影响到Promise外部的代码,通俗的说法是Promise被吃掉了。

    一般来讲总是建议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()方法。

    Promise.prototype.finally()

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

    promise
    .then(result => {···})
    .catch(error => {···})
    .finally(() => {···});
    

    上面代码中,不管promise 最后的状态,在执行then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。

    finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的Promise状态到底是fulfilled 还是 rejected。 这表明,finally 方法里面的操作,应该是与状态无关的,不依赖Promise 的执行结果。

    Promise.all()

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

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

    上面的代码中,all方法接受一个数组作为参数 , p1,p2,p3 都是Promise实例,如果不是就会使用resolve方法将他们变成promise实例,再进一步处理,另外Promise.all()方法的参数可以不是数组,但必须具有Iterator,接口且返回的没测成员都是Promise 实例。

    p 的状态有p1 , p2 ,p3 决定,有两种情况, 如果全是成功,p的状态才会是成功,第二只要有一个是失败,那么p的状态就是失败,并且会获取到第一个失败的例子

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值