题目
同步和异步的区别是什么?
手写Promise加载一张图片
前端使用异步的场景有哪些?
下方打印顺序
console.log(1)
setTimeout(function(){
console.log(2)
}, 1000);
console.log(3)
setTimeout(function(){ // 不管是几秒后执行,它也是个异步,先执行同步代码后再去执行,然后等待1秒后再执行另一个setTimeout
console.log(4)
}, 0);
console.log(5)
// 1 3 5 **4 2**
先执行4时间短,再执行2 因为要等1秒
知识点
单线程和异步
应用场景
callback hell和Promise
- callback本身没有问题,callback多了就嵌套成hell了,就有问题了;
- 不是解决callback本身,是解决callbakc嵌套
单线程和异步
单线程是异步的背景
JS是单线程语言,只能同时做一件事儿
浏览器和nodejs已支持JS启动进程,如Web Worker
JS和DOM渲染共用同一个进程,互相阻塞,因为JS可修改DOM结构
遇到等待(网络请求,定时任务)不能卡住
需要异步(解决单线程等待的问题)
回调callback函数形式
面试题一:同步和异步的区别是什么
基于JS单线程语言
异步不会阻塞代码执行
同步会阻塞代码执行
// 异步【通过callback回调函数来执行的】,不会阻塞后面的执行
console.log(100)
setTimeout(()=>{
console.log(200)
}, 1000)
console.log(300)
// 同步 alet(200)会卡住后面的代码
console.log(100)
alert(200)
console.log(300)
面试题二:应用场景(等待的场景)
等待的过程中,cpu不能闲着,所以需要异步,让后面的代码继续执行
- 网络请求,如ajax图片加载
- 定时任务,如setTimeout
// ajax
console.log('start');
$.get('./data1.json', function(data1){
console.log(data1);
})
console.log('end')
// 打印顺序
// start
// end
// data
解释
// 先打印 start
// 再执行网络请求,让它先请求,继续往下执行
// 再打印 end
// 等网络请求完,执行回调函数,最后打印data
console.log('start');
let img = document.createElement('img');
img.onload = function(){
console.log('loaded')
}
img.src = './xxx.png';
console.log('end')
// 打印顺序
// start
// end
// loaded
解释
// 先打印start
// 定义img
// 定义img.onload,就是callback的一种形式, 赋值成callback函数的形式
// src赋值,src一旦赋值之后,img.onload就会触发加载,
// 再打印end:img.src加载过程中,就不管了,就先打印 end,
// 等 img.src加载完成后,img.onload就会触发加载
// setTimeout
console.log(100)
setTimeout(function(){
console.log(200)
}, 1000)
console.log(300)
// 100
// 300
// 200
// setInterval
console.log(100)
setInterval(function(){
console.log(200)
}, 1000)
console.log(300)
// 100
// 300
// 200
// 200
// 200
...
call hell
异步是基于回调函数的形式执行的,越陷越深,但这个方式可读性很差,回调满天飞
$.get(url1, (data)=>{
console.log(data1)
// 获得第二份数据
$.get(url2, (data2)=>{
console.log(data2)
// 获得第三份数据
$.get(url3, (data3)=>{
console.log(data3)
// 还可能获取更多的数据
})
})
})
手写Promise 网络请求
- promise 将callback变成了管道串联形式,是非嵌套形式,而不是callback hell的嵌套形式,提高代码可读性
function getData(){
return new Promise((resolve, reject) => {
$.ajax({
url,
success(data){
resolve(data)
},
error(err){
reject(err)
}
})
})
}
const url1 = '/data1.json';
const url2 = '/data2.json';
const url3 = '/data3.json';
getData(url1)
.then((data1)=>{
console.log(data1)
return getData(url2)
})
.then((data2=>{
console.log(data2)
return getData(url3)
}))
.then((data3=>{
console.log(data3)
}))
.catch(err => console.log(err)) // 处理上方报错
面试题三:手写Promise加载图片
function loadImg(src){
return new Promise((resolve, reject) => {
let img = document.createElement('img');
// 图片加载完成后执行的回调函数img.onload,promise不是避免了回调函数,而是把回调函数这种形式改为串联的形式
img.onload = function(){
resolve(img)
}
img.onerror = function(){
const err =
reject(new Error(`图片加载失败 ${src}`))
}
// 这句话一赋值完成,
// 成功就触发上方的onload函数,调用 resolve把 img传出去
// 失败就触发onerror函数,调用 reject把 err抛出去
img.src = src;
})
}
// 1 用resolve
const src = '//www.baidu.com/img/flexible/logo/pc/result@2.png';
loadImg(src)
.then(img => {
console.log(img.width)
return img
})
.then(img => {
console.log(img.height)
})
.catch(err => console.log(err))
// 404
// 131
// 2 用reject
const src = '//1www.baidu.com/img/flexible/logo/pc/result@2.png';
loadImg(src)
.then(img => {
console.log(img.width)
return img
})
.then(img => {
console.log(img.height)
})
.catch(err => console.log(err))
// Error: 图片加载失败 //1www.baidu.com/img/flexible/logo/pc/result@2.png
// 3 两张图片
const src1 = '//www.baidu.com/img/flexible/logo/pc/result@2.png';
const src2 = 'https://img1.mukewang.com/5477304c00014a4c01320132-100-100.jpg'
loadImg(src1)
.then(img1 => {
console.log(img1.width)
return img1 // 普通对象,其后的then就接收这个普通对象
})
.then(img1 => {
console.log(img1.height)
// promise实例, 用promise来解决地狱回调的问题,其后的then接收这个实例的resolve中的参数
return loadImg(src2)
})
.then(img2 => {
console.log(img2.width)
return img2
})
.then(img2 =>{
console.log(img2.height)
})
.catch(err => console.log(err))
function getData(){
return new Promise((resolve, reject) => {
$.ajax({
url,
success(data){
resolve(data)
},
error(err){
reject(err)
}
})
})
}
// 404 // 第一张图的宽
// 132 // 第一张图的高
// 100 // 第二张图的宽
// 100 // 第二张图的高