浅谈js异步编程
js语言是单线程的
同步任务:在主线程上排队执行的任务,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的。
异步任务:不进入主线程、而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。所以说同步执行其实也是一种只有主线程的异步执行。
定时器:
setTimeOut(function(){
// 回调函数
}, 1000)
// 示例一
var fn = function() {
let num = Math.round(Math.random() * 100) / 100;
console.log(num);
setTimeout(function() {
if (num < 0.5) {
return ('1234567');
} else {
return ('abcdefg');
}
}, 500)
}
console.log(fn())
// 0.68 undefined
上面例子中setTimeOut为异步函数,执行的console.log(res())是同步的,不会等待计时器函数的执行。
回调函数:是一个作为变量传递给另外一个函数的函数,它在主体函数执行完之后执行。
简单的回调函数例子:
function fn(arg1, arg2, callback){
var num = Math.ceil(Math.random() * (arg1 - arg2) + arg2);
callback(num); //传递结果
}
fn(10, 20, function(num){
console.log("Callback called! Num: " + num);
});
//结果为10和20之间的随机数
使用回调函数解决示例一的问题:
var fn = function(res) {
let num = Math.round(Math.random() * 100) / 100;
console.log(num);
setTimeout(function() {
if (num < 0.5) {
res('1234567');
} else {
res('abcdefg');
}
}, 500)
}
var result = fn(function(res1) {
console.log(res1)
})
// 0.43 1234567
回调地狱:多层嵌套的回调函数
var sayhello = function (name, callback) {
setTimeout(function () {
console.log(name);
callback();
}, 1000);
}
sayhello("first", function () {
sayhello("second", function () {
sayhello("third", function () {
console.log("end");
});
});
});
// first second third end
-
代码阅读困难!
-
不能用 try catch 捕获错误,不能 return
-
调试困难,很难处理错误
-
耦合性过高,一旦有所改动,就会牵一发而动全身
**Promise对象:**解决了回调地狱,为异步操作提供统一接口。它起到代理作用,充当异步操作与回调函数之间的中介,使得异步操作具备同步操作的接口。Promise可以让异步操作写起来 就像在写同步操作的流程,而不必一层层地嵌套回调函数。
promise对象的创建与使用
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
promise对象的链式调用解决了回调地狱,每次 then 后返回的都是一个全新 Promise,如果我们在 then 中 return ,return 的结果会被 Promise.resolve() 包装
var sayhello = function (name) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(name);
resolve(); //在异步操作执行完后执行 resolve() 函数
}, 1000);
});
}
sayhello("first").then(function () {
return sayhello("second"); //仍然返回一个 Promise 对象
}).then(function () {
return sayhello("third");
}).then(function () {
console.log('end');
}).catch(function (err) {
console.log(err);
})
// first second third end
使用Promise对象解决示例一的问题:
const fn = function() {
var _this = this;
let num = Math.round(Math.random() * 100) / 100;
console.log(num);
return new Promise((resolve, reject) => {
setTimeout(function() {
num < 0.5 ? resolve('1234567') : reject('abcdefg')
}, 500);
})
}
fn()
.then(val => {console.log(val);})
.catch(err => {console.log(err)});
// 0.86 abcdefg
特点:Promise只会有一个解析结果:完成或拒绝,并且无法取消。
**Async/await:**是异步的终极解决方案
解决示例一的问题:
const fn = async function () {
let num = Math.round(Math.random() * 100) / 100;
console.log(num);
const result = await aa(num);
}
function aa(num) {
setTimeout(() => {
console.log(num < 0.5 ? '1234567' : 'abcdefg')
}, 500)
}
fn();
// 0.88 1234567
解决回调地狱:
var sayhello = function (name) {
setTimeout(function () {
console.log(name);
}, 1000);
}
async function fn() {
await sayhello('first')
await sayhello('second')
await sayhello('third')
await sayhello('end')
}
fn();
// first second third end
await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低;如果有依赖性,就是解决回调地狱的方式。