1.Promise
Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象,从它可以获取异步操作的消息,从而可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
Promise对象提供统一的接口,使得控制异步操作更加容易,它还提供了统一的API,各种异步操作都可以用同样的方法进行处理。
Promise构造函数接收一个函数作为参数,这个函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不用自己部署。如果调用resolve()函数和reject()函数时带有参数,那么它们的参数会被传递给回调函数。
resolve()函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从Pending变为Resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject()函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从Pending变为Rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法(then方法定义在原型对象Promise.prototype上)分别指定Resolved状态和Rejected状态的回调函数:
.then(function(){ //success }, functions(){ //error });
Promise.prototype.then方法的作用是为Promise实例添加状态改变时的回调函数。then方法的第一个参数是Resolved状态的回调函数,第二个参数(可选)是Rejected状态的回调函数。例如:
function getPromise(){ let promise = new Promise(function(resolve,reject){ setTimeout(()=>{ let random = Math.random(); if(random>0.5){ resolve(random); } else { reject(random); } },1000) }); return promise; } let promise = getPromise(); promise.then((result)=>{ //result为resolve(random)中的random console.log("success",result); },(error)=>{ //result为reject(random)中的random console.log("error",error); })
then方法返回的是一个经回调函数处理后的新的Promise实例,因此可以采用链式写法,即then方法后面再调用另一个then方法。如果使用两个then方法,第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。
Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。一般来说,不在then方法里面定义Reject状态的回调函数(即then的第二个参数),而是使用catch方法来处理发生错误时的情况。
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例:
let p = Promise.all([p1, p2, p3]);
上面代码中,Promise.all方法接收一个数组作为参数,p1、p2、p3都是Promise实例,p的状态由p1、p2、p3决定:
只有p1、p2、p3的状态都变成fulfilled(成功),p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值会传递给p的回调函数。
let p = Promise.all([p1,p2,p3]); p.then((result)=>{ console.log("全部成功",result); }) .catch((error)=>{ console.log("出现问题",error); })
Promise.any方法同样是将多个Promise实例,包装成一个新的Promise实例:
let p = Promise.any([p1, p2, p3]);
上面代码中,Promise.any方法接收一个数组作为参数,p1、p2、p3都是Promise实例,p的状态由p1、p2、p3决定:
当p1、p2、p3的状态有一个变成fulfilled,p的状态就会变成fulfilled,第一个状态为fulfilled的Promise实例就会将返回结果传递给p的回调函数。
当p1、p2、p3之中全部被rejected,p的状态才变成rejected,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
let p = Promise.any([p1,p2,p3]); p.then((result)=>{ console.log("有承诺成功",result); }) .catch((error)=>{ console.log("全部出现问题",error); })
Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。下面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变,那个率先改变的Promise实例的返回值就会传递给p的回调函数(race本身就有竞跑的意思)。
let p = Promise.race([p1, p2, p3]);
上面代码中,Promise.race方法接收一个数组作为参数,p1、p2、p3都是Promise实例,p的状态由p1、p2、p3决定:
当p1、p2、p3的状态有一个变成fulfilled,p的状态就会变成fulfilled,第一个状态为fulfilled的Promise实例就会将返回结果传递给p的回调函数。
当p1、p2、p3之中有一个被rejected,p的状态就变成rejected,第一个被reject的Promise实例就会将返回结果传递给p的回调函数。
p的状态取决于第一个状态改变的那个承诺对象的状态。
let p = Promise.race([p1,p2,p3]); p.then((result)=>{ console.log("有承诺成功",result); }) .catch((error)=>{ console.log("出现问题",error); })
Promise.resolve方法将现有对象转为Promise对象,例如:
// 基于promise的异步操作的封装 function get_promise(url){ // 将异步操作封装到一个承诺对象中 return new Promise((resolve,reject)=>{ let xhr = new XMLHttpRequest(); xhr.open("GET",url); xhr.responseType = "json"; xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); xhr.send(); xhr.onreadystatechange = function(){ if(this.readyState === 4){ if(this.status === 200){ // 承诺成功 resolve(this.response); } else { // 承诺失败 reject(this); } } } }) } let url = "http://134.175.100.63:6677/customer/findAll"; let $_ajax = $.get(url); console.log($_ajax) let p = Promise.resolve($_ajax); p.then((result)=>{ //result为resolve(this.response)中的this.response console.log("result:",result); })
当Promise.resolve方法不带有任何参数时,直接返回一个Resolved状态的Promise对象;当参数是一个Promise实例,Promise.resolve将不做任何修改、原封不动地返回这个实例;当参数是一个thenable对象(具有then方法的对象),Promise.resolve方法会将这个对象转为Promise对象,然后就立即执行thenable对象的then方法,例如:
let obj = { name:"terry", then:function(){ console.log("-----"); return "thenable" } } let p2 = Promise.resolve(obj); console.log("p2:",p2);
当参数不是具有then方法的对象,或根本就不是对象时,只是一个原始值时,Promise.resolve方法返回一个新的Promise对象,状态为Resolved。
Promise.reject(“reason”)方法也会返回一个新的Promise实例,该实例的状态为rejected。
let p = Promise.reject(“出错了”);
//等同于
let p = new Promise((resolve, reject) => reject(“出错了”));
Promise.prototype.finally方法用于指定无论Promise对象最后状态如何,最后都会执行的回调函数。常见应用有服务器处理Promise请求后,用finally结束加载状态:
get_promise(url); .then((result)=>{ console.log("请求成功1:",result); return result; }) .catch(()=>{ alert("出现异常"); }) .finally(()=>{ alert("请求结束"); })
2.Generator
Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同,可以把Generator函数理解成一个封装了多个内部状态的状态机。
语法上,Generator函数是一个普通函数,但是有两个特征:一是,function关键字与函数名之间有一个星号(一般紧贴function);二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”),后面可接值或函数/函数调用。例如:
function* helloWorldGenerator(){ yield ‘hello‘; yield ‘world’; return ‘ending‘; //一般情况下不加return返回值语句,因为它的done值为true //该函数有三个状态:hello、world和return语句(结束执行) } let hw = helloWorldGenerator();
调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是返回一个指向内部状态的指针对象,也就是遍历器对象(可以把Generator函数理解为等价于迭代器生成函数),它可以调用next方法:
hw.next(); //{ value: ‘hello‘, done: false } hw.next(); //{ value: ‘world‘, done: false } hw.next(); //{ value: ‘ending‘, done: true} hw.next(); //{ value: undefined, done: true }
由于Generator函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实是提供了一种可以暂停执行的函数,yield表达式就是暂停标志。
需要注意的是,yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行,因此等于为JavaScript 提供了手动的“惰性求值”的语法功能。
yield表达式只能出现在Generator函数中,它本身没有返回值,或者说总是返回undefined,而next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。该功能可以用在Generator函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为。例如:
let $ = { //基于promise的异步操作的封装 get(url){ //将异步操作封装到一个承诺对象中 return new Promise((resolve,reject)=>{ let xhr = new XMLHttpRequest(); xhr.open("GET",url); xhr.responseType = "json"; xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); xhr.send(); xhr.onreadystatechange = function(){ if(this.readyState === 4){ if(this.status === 200){ //承诺成功 resolve(this.response); } else { //承诺失败 reject(this); } } } }); } } function* foo(){ let c_url = "http://134.175.100.63:6677/customer/findAll"; let o_url = "http://134.175.100.63:6677/order/findAll"; let customers = yield call($.get,c_url); let order = yield call($.get,o_url); } //异步函数的执行器 function call(handler,params){ //将接收到的第二个参数作为第一个参数方法的参数 handler(params) .then((response)=>{ //在上一个请求结束后再去调用下一个请求 //response为resolve(this.response)的response //将当前请求结果作为yield表达式的返回值返回 //next参数作为上一个yield表达式的返回值,此处由customer变量接收 iterator.next(response); }) } let iterator = foo(); //Generator函数返回一个迭代器对象 iterator.next(); //手动让Generator函数往下执行语句
可以把异步操作写在yield表达式里,等到调用next方法的时候再往后执行。这实际上等同于不需要写回调函数了,因为异步操作的后续操作可以放在yield表达式下面,反正要等到调用next方法的时候才执行,体现异步操作的同步表达。例如:
function* loadUI(){ showLoadingScreen(); yield loadUIDataAsynchronously(); hideLoadingScreen(); } let loader = loadUI(); loader.next(); //加载UI loader.next(); //卸载UI
3.Async
ES2017标准引入了async函数,使得异步操作变得更加方便,它相当于Generator函数的语法糖(更方便易用的语法特性),使得Generator函数的应用更加简化,如可以将Generator函数中的函数foo例子简化为:
async function foo(){ let c_url = "http://134.175.100.63:6677/customer/findAll"; let o_url = "http://134.175.100.63:6677/order/findAll"; let customers = await $.get(c_url); let order = await $.get(o_url); //return xxx; } let promise = foo(); promise.then((result)=>{ //result为函数foo中return语句的值 })
Generator函数的执行必须靠执行器,所以才有了co模块(一种辅助的第三方模块),而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。
async和await比起星号和yield,语义更加清楚。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
yield命令后面只能是值、函数或Promise对象,而async函数的await命令后面可以是Promise对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即resolved的Promise对象)。
async函数的返回值是Promise对象,这比Generator函数的返回值是Iterator对象更加方便,可以直接使用then方法指定下一步操作。
async函数完全可以看作多个异步操作包装成的一个Promise对象,而await命令就是内部then命令的语法糖。
原文:https://www.cnblogs.com/wodeqiyuan/p/11662168.html