目录
Promise
引子:
// 分析ajax $.ajax({ url: '', method: '', data: {}, success(res){ console.log(1); }, error(error){ console.log(2); } }) console.log(3); // 这里面 3 最有可能是先被打印出来的 然后再打印出1 或 2 因为ajax是一个 //异步操作 js是自上而下运行的 所以ajax先执行网络强求(延时操作) 并不会阻 //拦js的执行 js继续往下执行 等ajax执行完毕后 才会输出1或2
AJAX默认是异步请求
根据真实项目案例,来说明传统异步编程的情况,和使用Promise编程的特点
//伪代码:传统解决方案 //获取年级,获取年级之后获取第一个年级的班级 $.ajax({ url:'XXX/grade', method:'get', data:{}, success(res){ //res.data 就是年级数组 $.ajax({ url:'XXX/clazz', method:'get', data:{gradeId:res.data[0].id}, success(res){ //res.data 就是班级数组 // 根据班级id 获取某一个同学的数据 // 我们现在仅仅是请求两个数据 还没有做 节点插入 数据处理 就需要嵌套这么多代码 如果再根据班级id或获取学生信息呢? 套娃结构 不推荐使用 不易阅读 不易修改 ...... $.ajax({}) }, error(err){} }); }, error(err){} }); //以上代码需要回调嵌套,不能并列去写,否则是对空值进行操作
// 为了解决这种问题 ES6给我们提供了几个异步编程的解决方案 // Promise 最基础的 掌握 // Generator 函数 高级 以Promise为基础 了解 // async函数 更高级的 以Promise为基础 了解 // 在vue中 我们会使用一个基于promise的网络工具 axios 内部封装了promise和http // async 异步操作 直接使用这个关键字就可以了 await 等一等 等待这个网络请求执行完毕后把结果返回给res 下面我们就可以拿到之前的数据进行操作了 async function test(){ let res1 = await axios.get('xxxx/grade');//年级数据 let res2 = await axios.get('xxxx/clazz',{params:{gradeId:res1.data[0].id}}); let res3 = await axios.get('xxxx/student',{params:{clazzId:res2.data[0].id}}); } test();
介绍
Promise 是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。
实例化
Promise
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
。它们是两个函数,由 JavaScript 引擎提供。Promise对象代表一个异步操作有三种状态: pending(进行中)、fulfilled(已成功)和rejected(已失败)。状态发生改变之后就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。const promise = new Promise(function(resolve, reject) { // ... some code if (/* 异步操作成功 */){ resolve(value); // pending -> fulfilled } else { reject(error); // pending -> rejected } });
原型方法
定义在
Promise.prototype
中的方法,通过Promise实例可以直接调用。Promise.prototype.then()
当状态由pending变为fulfilled的时候执行该回调函数, 参数: 最多需要有两个参数,Promise 的成功和失败情况的回调函数。 返回值: 返回一个新的Promise实例对象,因此可以使用链式调用。
当一个 Promise 完成(fulfilled)或者失败(rejected)时,返回函数将被异步调用(由当前的线程循环来调度完成)。具体的返回值依据以下规则返回。如果 then 中的回调函数:
返回了一个值,那么 then 返回的 Promise 将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。
没有返回任何值,那么 then 返回的 Promise 将会成为接受状态,并且该接受状态的回调函数的参数值为 undefined。
抛出一个错误,那么 then 返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。
返回一个已经是接受状态的 Promise,那么 then 返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。
返回一个已经是拒绝状态的 Promise,那么 then 返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。
返回一个未定状态(pending)的 Promise,那么 then 返回 Promise 的状态也是未定的,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。
let p1 = new Promise((resolve, reject) => { resolve('成功!'); // or // reject(new Error("出错了!")); }); p1.then(value => { console.log(value); // 成功! }, error => { console.log(error); // 出错了! });
Promise.prototype.catch()
当状态由pending变为rejected的时候执行该回调函数,
参数:
回调函数,回调函数的参数为reject函数传递过来的值
返回值:
返回一个新的Promise实例对象,因此可以使用链式调用。
/ 抛出一个错误,大多数时候将调用catch方法 let p1 = new Promise(function(resolve, reject) { throw 'Uh-oh!'; }); p1.catch(function(e) { console.log(e); // "Uh-oh!" });
推荐使用catch方法,不要在then方法中定义rejected状态的回调函数;这是因为使用catch还可以捕获在then方法执行中存在的错误。
// bad promise.then(function(data) { // success }, function(err) { // error }); // good promise.then(function(data) { // success }) .catch(function(err) { // error })
Promise.prototype.finally()
finally() 方法返回一个Promise。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。这为在Promise是否成功完成后都需要执行的代码提供了一种方式。这避免了同样的语句需要在then()和catch()中各写一次的情况。
参数:
回调函数,不接收任何参数
返回值:
返回一个新的Promise实例对象
let p1 = new Promise(function(resolve, reject) { throw 'Uh-oh!'; }); p1.catch(function(e) { console.log(e); // "Uh-oh!" }).finally(function() { console.log('这段代码最终都会执行'); });
静态方法
定义在Promise中的方法,通过Promise可以直接调用。
Promise.all([p1,p2])
Promise.all用于将多个 Promise 实例,包装成一个新的 Promise 实例, 参数: 数组,数组中的元素为Promise实例 返回值: Promise实例,当p1,p2状态都为fulfilled时候,该实例的状态才为fulfilled,此时p1,p2的返回值组成一个数组,传递给该实例的回调函数;只要p1,p2的返回值有一个变为rejected,该实例状态为rejected。
const promise1 = Promise.resolve(3); const promise2 = 42; const promise3 = new Promise((resolve, reject) => { setTimeout(resolve, 100, 'foo'); }); Promise.all([promise1, promise2, promise3]).then((values) => { console.log(values); }); // expected output: Array [3, 42, "foo"]
Promise.race([p1,p2])
Promise.race用于将多个 Promise 实例,包装成一个新的 Promise 实例, 参数: 数组,数组中的元素为Promise实例 返回值: Promise实例,当p1,p2之中有一个实例率先改变状态,该实例的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给该实例的回调函数。
const promise1 = new Promise((resolve, reject) => { setTimeout(resolve, 500, 'one'); }); const promise2 = new Promise((resolve, reject) => { setTimeout(resolve, 100, 'two'); }); Promise.race([promise1, promise2]).then((value) => { console.log(value); // Both resolve, but promise2 is faster }); // expected output: "two"
Promise.any([p1,p2])
用于将多个 Promise 实例,包装成一个新的 Promise 实例 参数: 数组,数组中的元素为Promise实例 返回值: Promise实例,只要p1,p2状态有一个变为fulfilled,该实例的状态为fulfilled;p1,p2状态都变为rejected,该实例状态才为rejected。
const pErr = new Promise((resolve, reject) => { reject("总是失败"); }); const pSlow = new Promise((resolve, reject) => { setTimeout(resolve, 500, "最终完成"); }); const pFast = new Promise((resolve, reject) => { setTimeout(resolve, 100, "很快完成"); }); Promise.any([pErr, pSlow, pFast]).then((value) => { console.log(value); // pFast fulfils first }) // expected output: "很快完成"
Promise.resolve()
用于将现有对象转化为Promise实例 参数:任意值
参数: 任意值
const promise1 = Promise.resolve(123); promise1.then((value) => { console.log(value); // expected output: 123 });
Promise.reject()
返回一个新的 Promise 实例,该实例的状态为rejected
参数: 错误信息
Promise.reject(new Error('fail')).then(function() { // not called }, function(error) { console.log(error); // Stacktrace });
应用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>promise</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script> var baseUrl = "http://47.106.244.1:8099" let p1 = new Promise((resolve, reject) => { /* $.get('http://47.106.244.1:8099/manager/category/findAllCategory',); */ $.ajax({ url: baseUrl+'/manager/category/findAllCategory', success(res) { resolve(res); }, error(err) { reject(err); } }) }) p1.then((res) => { console.log(res); // 对res操作 }).catch((err) => { console.log(err); }) </script> </head> <body> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>promise封装</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script> var baseUrl = "http://47.106.244.1:8099" // 封装工厂函数 function getPromise(url, method = "get", data = null) { return new Promise((resolve, reject) => { $.ajax({ url, method, data, success(res) { resolve(res); }, error(err) { reject(err); } }) }); } let p1 = getPromise(baseUrl+'/manager/category/findAllCategory'); let p2 = getPromise(baseUrl+'/manager/user/findAllUser'); /* p1.then((res) => { console.log(res, '---'); }) p2.then((res) => { console.log(res, '+++') }) */ // all 都成功就成功,有一个失败就失败 // race 谁快用谁的结果 // any 有成功就成功的,如果都失败就失败 let p = Promise.any([p1, p2]); p.then((res) => { console.log(res, '++++'); }).catch((err) => { console.log(err, '----'); }) </script> </head> <body> </body> </html>