首页 > 其他 > 详细

企业级开发(4)

时间:2019-10-12 16:11:35      阅读:81      评论:0      收藏:0      [点我收藏+]

六、异步编程解决方案:PromiseGenerator

1.Promise

Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象,从它可以获取异步操作的消息,从而可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。

Promise对象提供统一的接口,使得控制异步操作更加容易它还提供了统一的API,各种异步操作都可以用同样的方法进行处理。

Promise构造函数接收一个函数作为参数,这个函数的两个参数分别是resolvereject。它们是两个函数,由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方法接收一个数组作为参数,p1p2p3都是Promise实例,p的状态由p1p2p3决定

只有p1p2p3的状态都变成fulfilled(成功)p的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

只要p1p2p3之中有一个被rejectedp的状态就变成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方法接收一个数组作为参数,p1p2p3都是Promise实例,p的状态由p1p2p3决定:

p1p2p3的状态有一个变成fulfilledp的状态就会变成fulfilled,第一个状态为fulfilledPromise实例就会将返回结果传递给p的回调函数。

p1p2p3之中全部rejectedp的状态变成rejected,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

let p = Promise.any([p1,p2,p3]);
p.then((result)=>{
    console.log("有承诺成功",result);
})
.catch((error)=>{
    console.log("全部出现问题",error);
})

Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。下面代码中,只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变那个率先改变的Promise实例的返回值就传递给p的回调函数(race本身就有竞跑的意思)。

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

上面代码中,Promise.race方法接收一个数组作为参数,p1p2p3都是Promise实例,p的状态由p1p2p3决定

当p1p2p3的状态有一个变成fulfilledp的状态就会变成fulfilled,第一个状态为fulfilledPromise实例就会将返回结果传递给p的回调函数。

当p1p2p3之中有一个被rejectedp的状态就变成rejected,第一个被rejectPromise实例就会将返回结果传递给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函数的执行,与普通函数一模一样,只要一行。

asyncawait比起星号和yield,语义更清楚。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

yield命令后面只能是值、函数或Promise对象,而async函数的await命令后面可以是Promise对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即resolvedPromise对象)

async函数的返回值是Promise对象,这比Generator函数的返回值是Iterator对象更加方便,可以直接使用then方法指定下一步操作。

async函数完全可以看作多个异步操作包装成的一个Promise对象,而await命令就是内部then命令的语法糖。

 

企业级开发(4)

原文:https://www.cnblogs.com/wodeqiyuan/p/11662168.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!