在工作中经常遇到:需要拿到接口中的数据,而不是自己初始化在data中的数据。或者换一种思路,怎么在同步代码中拿到异步数据?
你先得弄明白js中的事件执行机制:
主线程执行的任务,称为同步任务,特点是:排队执行,只有前一个任务执行完毕,才能进行下一个任务。
任务队列执行的任务,称为异步任务,特点是:当异步任务到时间可以执行时,任务队列通知主线程,这一任务才会在主线程中排队等待执行。
执行流程:同步任务在主线程中执行,形成一个执行栈,执行完毕后;系统会读取任务队列排队的异步任务,而之前排队的任务队列中的异步任务结束等待状态,进入执行栈开始执行剩下的任务。
一个非常简单的应用场景:
let time
setTimeout(() => {
time = 60
console.log(time); // 60
}, 2000);
console.log(time); // undefined
思考下,怎么得到异步的结果 ?
1、回调callback()来实现,
让我们研究下这个setTimeout函数,setTimeout函数的常见语法:var timeoutID = scope.setTimeout(function[ , delay, arg1, arg2, ...]),例子如下:
let timeId = setTimeout((a,b,c) => {
console.log(a,b,c);
}, 1000,1,2,3);
let timeId2 = setTimeout((a,b,c) => {
console.log(a,b,c);
}, 1000,1,2,3);
let timeId3 = setInterval((a,b,c) => {
console.log(a,b,c);
}, 2000,1,2,3);
console.log(timeId,'timeId');
console.log(timeId2,'timeId2');
console.log(timeId3,'timeId3');
Tips: 执行结果会同步打印出 :1 'timeId' 、2 'timeId2' 、3 'timeId3' ;接着过1秒异步同步化执行 1 2 3 、1 2 3;再过1秒执行1 2 3;接着每隔2秒执行 1 2 3。函数返回值是一个定时器id(或者计时器id)且是一个正整数,验证了setTimeout() 和 setInterval() 共用一个id“编号池”。值得一提的是,这个延迟的时间并不准确,存在最小延迟时间。
这里留个问题,有兴趣可以研究下为什么存在这个延迟时间还有如何解决这个问题?
setTimeout()中第一个参数是个函数,也称高阶函数,但更习惯叫它回调函数。
function fun(fn){
fn(0)
setTimeout(() => {
fn(1)
setTimeout(() => {
fn(2)
}, 1000);
}, 1000);
}
fun((val)=>{
console.log(val);
})
Tips: 执行结果会同步打印出 :0;过1秒后打印:1;再过1秒后打印:2。
let num = {a:1}
function fun(fn){
fn(0)
setTimeout(function(){
fn(this)
}.bind(num), 1000);
}
fun((val)=>{
console.log(val);
})
Tips: 执行结果会同步打印出 :0;过1秒后打印:{a:1}。 因为箭头函数中没有this的指向,所以把 setTimeout的回调函数改成箭头函数则不能使用强绑定来写会报错!上面的代码也说明了bind的独特性:并没有立即执行函数,若改成call和apply,执行结果会变成同步打印 :0、{a:1}。
回归正题;
function fun(fn){
setTimeout(() => {
fn(2)
}, 2000);
}
fun((res)=>{
console.log(res);
})
Tips: 执行结果会异步2秒后打印出:2。即:用回调函数拿到异步数据。
2、链式调用.then()
// 写法1
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 2000);
})
p.then(res=>{
console.log(res);
})
// 写法2
let a = 1
function getData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let a = 10
resolve(a)
}, 2000);
})
}
getData().then(res=>{
console.log(res,'resssss');
})
Tips: 执行结果会异步2秒后打印出:2。即:用链式调用拿到异步数据。
3、async和await
// 写法1
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 2000);
});
// IIFE
(async function() {
let res = await p
console.log(res);
})()
// 写法2
let a = 1
function getData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let a = 10
resolve(a)
}, 2000);
})
}
!async function () {
let res = await getData()
console.log(res, 'ress');
}()
Tips: 执行结果会异步2秒后打印出:2。即:用async、await拿到异步数据。值得注意的是,在立即执行函数(IIFE)前面的分号如果忘记写了,就报错!!!
番外篇:模拟后端异步数据
分别用回调函数、then()、async模拟接口返回的数据,怎么处理?
回调函数写法:
function getData(params) {
return function (callback) {
data = {
code: 200,
type: 'Get',
message: '访问成功',
id: params.id,
data: [{
name: '武汉市',
time: '2021',
number: 2000
},
{
name: '宜昌市',
time: '2022',
number: 1000
},
]
}
setTimeout(() => {
callback(data)
}, 2000);
}
}
function conduct() {
let params = {
id: 100
}
// 回调函数写法
getData(params)(res => {
console.log(res); // {code: 200, type: 'Get', message: '访问成功', id: 100, data: Array(2)}
})
}
conduct()
then写法:
function getData(params) {
return new Promise((resolve, reject) => {
let data = {
code: 200,
type: 'Get',
message: '访问成功',
id: params.id,
data: [{
name: '武汉市',
time: '2021',
number: 2000
},
{
name: '宜昌市',
time: '2022',
number: 1000
},
]
}
setTimeout(() => {
resolve(data)
}, 2000);
})
}
function conduct() {
let params = {
id: 100
}
// then写法
getData(params).then(res => {
console.log(res); // {code: 200, type: 'Get', message: '访问成功', id: 100, data: Array(2)}
})
}
conduct()
async写法:
function getData(params) {
return new Promise((resolve, reject) => {
let data = {
code: 200,
type: 'Get',
message: '访问成功',
id: params.id,
data: [{
name: '武汉市',
time: '2021',
number: 2000
},
{
name: '宜昌市',
time: '2022',
number: 1000
},
]
}
setTimeout(() => {
resolve(data)
}, 2000);
})
}
// async写法
async function conduct() {
let params = {
id: 100
}
let res = await getData(params)
console.log(res); // {code: 200, type: 'Get', message: '访问成功', id: 100, data: Array(2)}
}
conduct()
突然让我联想到,我自己经常犯的理解误区,Dom机制和异步编程的基本逻辑混淆,这个边缘知识点,以后记起来再进行补充说明!
以上就是,我对JavaScript开发过程中的异步编程里面的一点理解!