我们知道js是有同步和异步任务的,这里简单的介绍以下它们的概念:
同步
:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
异步
:不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
可以这么说,在目前最简单的异步任务就是定时器了。
好了,切入主题,今天主要讲到关于异步回调函数的成为同步处理及优化的过程;
在最初我们处理回调函数时,就是下面的处理:
var sayhello = function (name, callback) {
setTimeout(function () {
console.log(name);
callback();
}, 1000);
}
sayhello("我是第一个", function () {
sayhello("我是第二个", function () {
sayhello("我是第三个", function () {
console.log("我是最后一个");
});
});
});
//输出: 我是第一个 我是第二个 我是第三个 我是最后一个
这样在我们回调函数过多的情况下,会一层层嵌套,这样的代码着实让人头大,我们可以叫它回调地狱。
那既然提出了问题,就得解决问题啊,于是这时一代救世主出现:Promise
简单介绍:Promise是一个构造函数,其原型上有then、catch方法,对象上有reject、resolve方法。通过new关键字创建Promise对象
- resolve:异步操作执行成功后的回调函数
- reject:异步操作执行失败后的回调函数
//链式实现同步依次执行
function runAsync1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("我是第一个吗?");
resolve("我要成为第一");
}, 5000)
})
}
function runAsync2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("我是第二个吗?");
resolve("我要成为第二");
}, 2000)
})
}
function runAsync3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("我是最后一个吗?");
resolve("我成为了最后");
}, 0)
})
}
runAsync1().then(data1 => {
console.log(data1);
return runAsync2();
}).then(data2 => {
console.log(data2);
return runAsync3();
}).then(data3 => {
console.log(data3);
});
它可以通过链式编程,即使你有再多的回调函数,我都可以通过then
来获取执行同时达到了同步执行任务的目的。
但是,这样看着还是有点别扭,这么多链子,而且每一次都要返回一个回调函数以获取下一个回调函数任务,略显繁琐,于是第二代救世主诞生:async
。
async(异步函数):它既解决了异步编程回调地狱问题,又不会显得繁琐。
简单介绍:
- 在普通函数定义前面加上async关键字,普通函数变为了异步函数;
- 异步函数默认的返回值是一个
promise对象
(不是undefined); - 可以使用
throw关键字
将错误抛出。
//函数加了async,返回值会默认封装成promise对象
//无论是基本数据类型 还是引用型数据类型都是自动会封装成promise对象
async function test1() {
return { name: "小王" }
}
async function test2() {
return function () { console.log(11); }
}
async function test3() {
return [1];
}
async function test4() {
return 123;
}
console.log(test1()); //Promise {<fulfilled>: {…}}
console.log(test2()); //Promise {<fulfilled>: ƒ}
console.log(test3()); //Promise {<fulfilled>: Array(1)}
console.log(test4()); //Promise {<fulfilled>: 123}
从上验证返回的是一个promise对象。
await:async 函数中可能会有 await 表达式,async 函数执行时,如果遇到 await 就会先暂停执行 ,等到触发的异步操作完成后,恢复 async 函数的执行并返回解析值。
await 操作符用于等待一个 Promise 对象, 它只能在异步函数 async function 内部使用。如果在 async function 函数体外使用 await ,你只会得到一个语法错误。
async function test() {
throw "呀,发生错误!!!";
}
test().catch(error => console.log(error)); //"呀,发生错误!!!"
我们看看它是如何进行优化回调问题的。
//async 函数诞生,优化了promise的繁琐链式调用
function runAsync1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("我是第一个吗?");
resolve("我要成为第一");
// reject("发生错误!!!")
}, 5000)
})
}
function runAsync2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("我是第二个吗?");
resolve("我要成为第二");
}, 2000)
})
}
function runAsync3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("我是最后一个吗?");
resolve("我成为了最后");
}, 0)
})
}
//对比前面,没有繁琐的then链式,更加简单轻便了
async function getRes() {
let res1 = await runAsync1();
console.log(res1);
let res2 = await runAsync2();
console.log(res2);
let res3 = await runAsync3();
console.log(res3);
}
getRes();
从结果上看,我们获得了像promise一样的结果,但它大大减少了我们的代码量。