Promise
Promise是ES6引入的异步编程的新解决方案,语法上Promise是一个构造函数,用来封装异步操作并可能获取成功或失败的结果,利用promise可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,promise对象提供统一的接口,使得控制异步操作更加容易。
//实例化promise对象
const p = new Promise(function(resolve,reject){
setTimeout(function(){
let data='数据库中的数据';
resolve(data)
let err='数据读取失败';
reject(err);
},1000);
});
//调用promise对象的then方法
p.then(funtion(value){
console.log(value)
},function(reason){
console.error(reason)
})
promise自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法。
resolve是对promise成功时候的回调,它把promise的状态修改为fullfiled,那么,reject就是失败的时候的回调,他把promise的状态修改为rejected,这样我们在then中就能捕捉到,然后执行“失败”情况的回调。
async/await
async用于申明一个function是异步的,而await则可以认为是 async/await的简写形式,是等待一个异步方法执行完成的。
async/awiat的使用规则:
- async 表示这是一个async函数, await只能用在async函数里面,不能单独使用
- async 返回的是一个Promise对象,await就是等待这个promise的返回结果后,再继续执行
- await 等待的是一个Promise对象,后面必须跟一个Promise对象,但是不必写then(),直接就可以得到返回值
//使用场景
//摇色子方法
function dice(){
return new Promise((resolve,reject)=>{
let sino = parseInt(Math.random()*6+1) //生成一个1~6之间的随机小数
setTimeout(()=>{
resolve(sino)
},2000)
})
}
//异步方法
async function text(){
let n= await dice()
//await 关键字后面调用摇色子方法执行完毕之后,才进行变量赋值
console.log("摇出来"+n) //最后打印出摇出来的数
}
text()
上面这段代码async中使await dice()先执行,等到三秒后执行完再把得到的结果赋值给左边的n,也就是说text函数需要两秒钟才执行完成,所以text函数是异步的,因此前面必须写async.
async/await的优点
- 方便级联调用:即调用依次发生的场景;
- 同步代码编写方式:Promise使用then函数进行链式调用,一直点点点,是一种从左向右的横向写法;async/await从上到下,顺序执行,就像写同步代码一样,更符合代码编写习惯;
- 多个参数传递:Promise的then函数只能传递一个参数,虽然可以通过包装成对象来传递多个参数,但是会导致传递冗余信息,频繁的解析又重新组合参数,比较麻烦;async/await没有这个限制,可以当做普通的局部变量来处理,用let或者const定义的块级变量想怎么用就怎么用,想定义几个就定义几个,完全没有限制,也没有冗余工作;
- 同步代码和异步代码可以一起编写:使用Promise的时候最好将同步代码和异步代码放在不同的then节点中,这样结构更加清晰;async/await整个书写习惯都是同步的,不需要纠结同步和异步的区别,当然,异步过程需要包装成一个Promise对象放在await关键字后面;
- 基于协程:Promise是根据函数式编程的范式,对异步过程进行了一层封装,async/await基于协程的机制,是真正的“保存上下文,控制权切换……控制权恢复,取回上下文”这种机制,是对异步过程更精确的一种描述;
- async/await是对Promise的优化:async/await是基于Promise的,是进一步的一种优化,不过在写代码时,Promise本身的API出现得很少,很接近同步代码的写法
function getJSON() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let json = Math.random() * 2
if (json > 1) {
resolve(json)
} else {
reject(json)
}
}, 2000)
})
}
const makeRequest = () =>
getJSON()
.then(data => {
console.log('data==>', data)
return 'done'
})
.catch(err => {
console.log('err==>', err)
})
makeRequest()
async/await的优点
- 方便级联调用:即调用依次发生的场景;
- 同步代码编写方式:Promise使用then函数进行链式调用,一直点点点,是一种从左向右的横向写法;async/await从上到下,顺序执行,就像写同步代码一样,更符合代码编写习惯;
- 多个参数传递:Promise的then函数只能传递一个参数,虽然可以通过包装成对象来传递多个参数,但是会导致传递冗余信息,频繁的解析又重新组合参数,比较麻烦;async/await没有这个限制,可以当做普通的局部变量来处理,用let或者const定义的块级变量想怎么用就怎么用,想定义几个就定义几个,完全没有限制,也没有冗余工作;
- 同步代码和异步代码可以一起编写:使用Promise的时候最好将同步代码和异步代码放在不同的then节点中,这样结构更加清晰;async/await整个书写习惯都是同步的,不需要纠结同步和异步的区别,当然,异步过程需要包装成一个Promise对象放在await关键字后面;
- 基于协程:Promise是根据函数式编程的范式,对异步过程进行了一层封装,async/await基于协程的机制,是真正的“保存上下文,控制权切换……控制权恢复,取回上下文”这种机制,是对异步过程更精确的一种描述;
- async/await是对Promise的优化:async/await是基于Promise的,是进一步的一种优化,不过在写代码时,Promise本身的API出现得很少,很接近同步代码的写法
function getJSON() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let json = Math.random() * 2
if (json > 1) {
resolve(json)
} else {
reject(json)
}
}, 2000)
})
}
const makeRequest = async () => {
const value = await getJSON()
console.log(value)
return value
}
makeRequest()
拓展运算符 (涉及浅拷贝)
对象的扩展运算符
对象中的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中
````js` let bar = { a: 1, b: 2 }; let baz = { ...bar }; // { a: 1, b: 2 }
述方法实际上等价于:
```js
let bar = { a: 1, b: 2 };
let baz = Object.assign({}, bar); // { a: 1, b: 2 }
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。(如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性)。
同样,如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。
let bar = {a: 1, b: 2};
let baz = {...bar, ...{a:2, b: 4}}; // {a: 2, b: 4}
扩展运算符对对象实例的拷贝属于一种浅拷贝 javascript中有两种数据类型,分别是基础数据类型和引用数据类型。
- 基础数据类型是按值访问的,常见的基础数据类型有Number、String、Boolean、Null、Undefined,这类变量的拷贝的时候会完整的复制一份;不存在深拷贝和浅拷贝的问题
let obj1 = { a: 1, b: 2};
let obj2 = { ...obj1, b: '2-edited'};
console.log(obj1); // {a: 1, b: 2}
console.log(obj2); // {a: 1, b: "2-edited"}
//拷贝的对象是*基础数据类型*,因此对`obj2`的修改并不会影响`obj1`
- 引用数据类型比如Array、Object,在拷贝的时候拷贝的是对象的引用,当原对象发生变化的时候,拷贝对象也跟着变化.只拷贝了地址,没有拷贝数据
let obj1 = { a: 1, b: 2, c: {nickName: 'd'}};
let obj2 = { ...obj1};
obj2.c.nickName = 'd-edited';
console.log(obj1); // {a: 1, b: 2, c: {nickName: 'd-edited'}}
console.log(obj2); // {a: 1, b: 2, c: {nickName: 'd-edited'}}
// 对`obj2`的修改影响到了被拷贝对象`obj1`
数组的扩展运算符
- 可以将数组转换为参数序列
function add(x, y) {
return x + y;
}
const numbers = [4, 38];
add(...numbers) // 42
- 可以复制数组
const arr1 = [1, 2];
const arr2 = [...arr1];
- 扩展运算符可以与解构赋值结合起来,用于生成数组
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
注意如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
const [...rest, last] = [1, 2, 3, 4, 5];
// 报错
const [first, ...rest, last] = [1, 2, 3, 4, 5];
// 报错
- 扩展运算符还可以将字符串转为真正的数组
[...'hello']
// [ "h", "e", "l", "l", "o" ]