前端分享----JS异步编程+ES6箭头函数

时间:2022-05-23 07:54:03

前端分享----JS异步编程+ES6箭头函数


##概述Javascript语言的执行环境是"单线程"(single thread)。所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务。

这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。

JavaScript语言本身并不慢,慢的是读写外部数据,比如等待Ajax请求返回结果。这个时候,如果对方服务器迟迟没有响应,或者网络不通畅,就会导致脚本的长时间停滞。

为了解决这个问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。"同步模式"就是传统做法,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的。这往往用于一些简单的、快速的、不涉及读写的操作。

"异步模式"则完全不同,每一个任务分成两段,第一段代码包含对外部数据的请求,第二段代码被写成一个回调函数,包含了对外部数据的处理。第一段代码执行完,不是立刻执行第二段代码,而是将程序的执行权交给第二个任务。等到外部数据返回了,再由系统通知执行第二段代码。所以,程序的执行顺序与任务的排列顺序是不一致的、异步的。

以下总结了"异步模式"编程的几种方法,理解它们可以让你写出结构更合理、性能更出色、维护更方便的JavaScript程序。##实际应用场景在项目的编辑页面,有很多时候会出现下拉,并且下拉框是通过请求后台获取的数据,但是会出现一种情况大家可能有遇到过,就是下拉框回填不成功,可能大家已经都清楚了原因就是因为:当请求的详情信息还没返回,请求的下拉接口却已经先返回来了,说直白一点,就是程序的执行先后顺序问题。那么怎么解决这种问题呢?##常见的解决方案###1.用延时来推测结果大概什么时候!

var result=ajax(url);
setTimeout(function(result){
console.log(result);
},400);

优点:几乎无弊端:尽量可能设置长时间延时,否则,我们将会操作一个undefined的result,可以说这是一种最不推荐并且反对的写法###2.回调函数!js最常见的一种规定程序执行的先后顺序的处理就是采用回调函数,可能java的同学不是很明白,其实很多地方都应用到了,比如:ajax的成功之后调用success方法,失败调用error方法,这种就叫回调函数,在某个时间点,数据返回来了,就执行。所以,通常遇到实际场景就会采用回调函数的写法,把第二个ajax写在第一个ajax的success里面,等第一个ajax请求返回结果后,在执行第二个。这种方法俗称函数嵌套。能解决这种情况。但是既然叫函数嵌套,嵌套二字难免让人感觉冗余,没错。

ajax(url0,function(result0){
ajax(result0.url1,function(result1){
ajax(result1.url2,function(result2){
console.log(result2);
});
});
});

如果嵌套有3个甚至更多,那就出现了回调地狱(Callback Hell)问题。如何规避这种问题呢?答案是就是:promise/a+规范,下面我们看一下promise/a+规范在了解promise之前我们先来了解一个链式写法;##链式写法链式写法也是我们在使用jQuery时候常见的一种,

$('#tab').eq($(this).index()).show().siblings().hide();

没错,这个就是我们所谓的链式写法,只是大家不知道书名而已;之所以叫链式写法,不难看出我们在一个元素上做的所有jQuery处理都可以一直 "." 下去, 为什么能这样,因为jQuery源码封装的时候,每个方法都返回this(jQuery)。

var obj = {
step1:function(){
console.log('a');
return this;
},
step2:function(){
console.log('b');
return this;
},
step3:function(){
console.log('c');
return this;
},
step4:function(){
console.log('d');
return this;
}
}

console.log('-----\n');
obj.step1().step2().step3();
console.log('-----\n');
obj.step4().step2().step1();

执行结果复制到控制台查看。这样的写法是不是看着很爽,那么与我们要讲的promise有什么关系呢?下面进入主题PromiseES 6中原生提供了Promise对象,Promise对象代表了某个未来才会知道结果的事件(一般是一个异步操作),并且这个事件对外提供了统一的API,可供进一步处理。Promise对象有三种状态,分别是pending-进行中、resolved-已完成、rejected-已失败当Promise的状态由pending转变为resolved或rejected时,会执行相应的方法,并且状态一旦改变,就无法再次改变状态,这也是它名字promise-承诺的由来。Promise的构造函数接受一个函数作为参数,这个函数的两个参数分别是resolve方法和reject方法。如果异步操作成功,就是用resolve方法将Promise对象的状态从“未完成”变为“完成”(即从pending变为resolved),如果异步操作出错,则是用reject方法把Promise对象的状态从“未完成”变为“失败”(即从pending变为rejected)

###Promise.prototype.then()Promise的then方法返回的是一个新的Promise对象,所以这样呢,我们就能链式操作了,同时then方法它包含两个参数方法,分别是已成功resolved的回调和已失败rejected的回调,分别是状态看一个例子吧!

const p = function(){
let num = Math.random();
return new Promise((resolve, reject) => {
setTimeout(() => {
num > 0.1 ? resolve(num) : reject(num);
}, 1000);
})
};
const p1 = function(val){
let num = Math.random();
return new Promise((resolve, reject) => {
setTimeout(() => {
num > 0.01 ? resolve(num) : reject(num);
}, 1000);
})
};


p().then(val => {
console.info(`Status1:fulfilled, value1:${val}`);
return p1(val);
}, val => {
console.info(`Status1:reject,value1:${val}`);
}).then(val => {
console.info(`Status2:fulfilled, value2:${val}`);
},
/*function(val1){console.log(val1); },*/val => {
console.info(`Status2:reject,value2:${val}`);
})

那么每个then方法都要写一个成功的方法和失败的方法貌似有点冗余啊,还好,Promise提供了一个catch()方法,.catch()的作用是捕获Promise的错误,与then()的rejected回调作用几乎一致,由于Promise的抛错具有冒泡性质,能够不断传递,这样就能够在下一个catch()中统一处理这些错误。同时catch()也能够捕获then()中抛出的错误,所以建议不要使用then()的rejected回调,而是统一使用catch()来处理错误。所以上面代码改为

p().then(val => {
console.info(`Status1:fulfilled, value1:${val}`);
return p1(val);
}).then(val => {
console.info(`Status2:fulfilled, value2:${val}`);
}).catch(function(err) {
console.log('出错:' + err); // 出错:reject // 最后的catch()方法可以捕获在这一条Promise链上的异常
})

好了!你们下来可以试着去这样用用Promise。

##ES6箭头函数一个常见的问题是,ECMAScript 和 JavaScript 到底是什么关系?不是很重要,所以直白一点,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现。而ES6,就是ECMAScript的一个版本而已,于2015年发布第一版。

1.具有一个参数的简单函数

var f = v => v;
//上面的箭头函数等同于:
var f = function(v) {
return v;
};

2.没有参数的需要用在箭头前加上小括号

//如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
var f = () => 5;
// 等同于
var f = function () { return 5 };
 

3.多个参数需要用到小括号,参数间逗号间隔,例如两个数字相加

//如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};

4.函数体多条语句需要用到大括号

var add = (a, b) => {
if (typeof a == 'number' && typeof b == 'number') {
return a + b
} else {
return 0
}
}

5.返回对象时需要用小括号包起来,因为大括号被占用解释为代码块了

var getHash = arr => {({
name: 'Jack',
age: 33
})}

再多啰嗦一个东西:解构赋值

requestLogin(loginParams).then(data => {
this.logining = false;
let { msg, code, user, list} = data;
if (!list.length) {
this.$message({
message: '登录失败',
type: 'error'
});
}
});
//等同于
requestLogin(loginParams).then(data => {
var msg=data.msg;
var list=data.list;
var code=data.code;
if (!list.length) {
this.$message({
message: '登录失败',
type: 'error'
});
}
});

是不是觉得还是比较方便简洁些了。