javaScript-系统知识点【同步和异步】
- 软件开发
- 2025-09-21 09:30:02

1. 同步和异步的区别?
基于 JS 是单线程语言
异步不会阻塞代码执行同步会阻塞代码执行 1.1 什么是同步、异步?同步: 按代码顺序执行
异步: 简单来说, 不按照代码顺序执行, 就是异步
1.2 为什么会有异步?异步是为了解决, JS 单线程阻塞问题的
1.3 如何 异步 解决 JS 单线程阻塞问题?通过 事件循环 来解决, 事件循环的执行流程, 同步任务会进入主线程执行, 而异步任务会进入任务队列, 等到主线程任务执行完, 任务队列的任务就会放入主线程执行, 如此循环反复
1.4 JS 如何实现异步?异步在于创建宏任务和微任务, 通过事件循环机制实现异步机制
宏任务
定时器 setTimeout、setInterval事件监听 (发布订阅 postMessage)回调函数I/O微任务
Promiseasync/await标准回答 (按异步编程进化史来说)
所有异步任务都是在同步任务执行结束之后,从任务队列中依次取出执行。
回调函数是异步操作最基本的方法,比如 AJAX回调,回调函数的优点是简单、容易理解和实现,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,使得程序结构混乱、流程难以追踪(尤其是多个回调函数嵌套的情况),而且每个任务只能指定一个回调函数。此外它不能使用 try catch 捕获错误
return Promise 包装了一个异步调用并生成一个 Promise 实例,当异步调用返回的时候根据调用的结果分别调用实例化时传入的resolve 和 reject方法,then接收到对应的数据,做出相应的处理。Promise不仅能够捕获错误,而且也很好地解决了回调地狱的问题,缺点是无法取消 Promise,错误需要通过回调函数捕获。
Generator(迭代器) 函数是 ES6 提供的一种异步编程解决方案,Generator 函数是一个状态机,封装了多个内部状态,可暂停函数, yield可暂停,next方法可启动,每次返回的是yield后的表达式结果。优点是异步语义清晰,缺点是手动迭代Generator函数很麻烦,实现逻辑有点绕
async/await是基于Promise实现的,async/await使得异步代码看起来像同步代码,所以优点是,使用方法清晰明了,缺点是await 将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了 await 会导致性能上的降低,代码没有依赖性的话,完全可以使用 Promise.all 的方式。
加分回答 JS 异步编程进化史:callback -> promise -> generator/yield -> async/await。 async/await 函数对 Generator 函数的改进
体现在以下三点: - 内置执行器。 Generator 函数的执行必须靠执行器,而 async 函数自带执行器。
也就是说,async 函数的执行,与普通函数一模一样,只要一行。 更广的适用性。
yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作), 更好的语义。
async 和 await,比起 星号 和 yield,语义更清楚了
async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。 目前使用很广泛的就是 promise 和 async/await
2. 手写用 Promise 加载一张图片 // 加载函数 ... function loading (src) { return new Promise( (resolve, reject) => { const img = document.createElement('img') img.onload = () => { resolve(img) } img.onerror = () => { const err = new Error(`图片加载失败 ${src}`) reject(err) } img.src = src } ) } const url1 = ' img /img1.jpg' const url2 = ' img /img2.jpg' loadImg(url1).then(img1 => { console.log(img1.width) return img1 // 普通对象 }).then(img1 => { console.log(img1.height) return loadImg(url2) // promise 对象 }).then(img2 => { console.log(img2.width) return img2 }).then(img2 => { console.log(img2.height) }).catch(ex => console.error(ex)) 3. 前端使用异步的场景有哪些?场景
网络请求定时任务 4. 读代码 // setTimeout 笔试题 console.log(1) setTimeout(function() { console.log(2) }, 1000) console.log(3) setTimeout(function() { console.log(4) }, 0) console.log(5) // 执行结果 1 3 5 4 2下面就是相关知识点
知识点:
单线程和异步
应用场景
callback hell (回调地狱) 和 Promise
(一)
单线程和异步
JS 是单线程语言, 只能同时做一件事儿浏览器和 nodejs 已支持 JS 启动进程, 如 Web WorkerJS 和 DOM 渲染共同一个线程, 因为 JS 可修改 DOM 结构遇到等待 (网络请求, 定时任务) 不能卡住, 所以需要异步, 以回调 callback 函数形式 // 异步 console.log(100) setTimeout(function () { console.log(200) }, 1000) console.log(300) // 输出结果 100、300、200 // 同步 console.log(100) aleart(200) console.log(300) // 输出结果 100、200、300(二)
异步的应用场景
网络请求, 如 ajax 图片加载定时任务, 如 setTimeout网络请求
// 网络请求 ajax console.log('start') $.get('./data1.json', function (data1) { console.log(data1) }) console.log('end') // 执行结果 start end data1 // 图片懒加载 console.log('start') let img = document.createElement('img') img.onload = function() { console.log('loaded') } img.src = '/xxx.png' console.log('end') // 执行结果 start end loaded定时任务
// 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 ...(三)
callback hell (回调地狱)
// 获取第一份数据 $.get(url1, (data1) => { console.log(data1) // 获取第二份数据 $.get(url2, (data2) => { console.log(data2) // 获取第三份数据 $.get(url3, (data3) => { console.log(data3) // 还可能获取更多的数据 }) }) })解决回调地狱的方案就是, Promise
// Promise 定义 function getData(url) { return new Promise((resolve, reject) => { $.ajax({ url, success(data) { resolve(data) }, error(err) { reject(err) } }) }) } // Promise 使用 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.error(err)) let approvalProcess = (name. time) => { return new Promise((resolve, reject) => { setTimeout(() => { // setTimeout 模拟异步 let approvalRes = Math.random() >= 0.2 // 随机数模拟异步成功操作成功或失败 if (approvalRes) { resolve(name+'已审核通过') } else { reject('审核不通过') } }, time) }) } let boss = approvalProcess('老板', 1000) let manager = approvalProcess('经理', 2000) boss.then(res => { console.log(res) return manager }).then(res => { console.log(res) return '老板和经理都通过了' }).then(result => { console.log(result) }).catch(err => { console.log(err) })[扩展] - promise 经典面试题
5. PromisePromise 的状态?
Promise 有三种状态:pengding、fulfilled、rejected
Promise 的状态一经改变,便不可以修改
var pro = new Promise( (resolve, reject) => { reject() resolve() }) pro.then( () => { console.log('resolve1') }). catch( () => {console.log('catch1') }) // reject1Promise 链式调用
Promise 的链式调用,有三个 Promise.prototype.then()、Promise.prototype.catch() 和 Promise.prototype.finally()
Promise.prototype.then() then 方法可以接收两个回调函数作为参数,第一个参数 resolve() 返回的数据,第二个参数 reject() 返回的数据
当然了,异常也会被第二个参数接收
.finally() 一定会执行,但是它没有回调参数
.then() 可有多个,.catch() 也可以有多个,但是 .then() 或者 .catch() 必须返回一个 Promise 才可以这样做
数据的接收顺序
then -> catch -> finally var pro = new Promise( (resolve, reject) => { reject() resolve() }) pro.then( () => { console.log('resolve1') }, () => { console.log('reject1') }). catch( () => {console.log('catch1') } ) // reject1只有 .then() 的第二个参数传,reject() 返回的数据 或者是 异常才会进到 .catch() 中
[注意] Promise 抛出异常是不会,直接中断的,会进入 .then() 的第二个参数,没有 .then() 的第二个参数才会 进入 .catch() 中
Promise 如果接收错误
catchthen 的第二个回调函数参数Promise 的一些方法
Promise.resolve() 返回 成功状态
Promise.reject() 返回 失败状态
Promise.finally() 不管什么状态都执行
Promise.then() 成功回调
Promise.catch() 错误回调
Promise.all() 一个 reject(), 整个结束执行 (获取全部都成功,再返回)
Promise.allSettled() 全部状态变更,才执行结束
Promise.any() 一个 resolve(),整个再返回 (获取全部都失败,再返回)
Promise.race() 那个状态先改变,那个先返回
await 后面可以跟 Promise 对象、非 Promise 值以及另一个 await 表达式。
await 后面也可以跟非 Promise 值,如基本数据类型(number、string、boolean 等)、对象、数组等。在这种情况下,await 会将该值直接返回,就好像该值被包装在一个已经解决的 Promise 中。
理解 JavaScript 的 async/await
5.1 两个异步请求如何合并?使用 Promise
//定义两个http请求方法 const getList1 = ()=>{ return new Promise((res,rej) =>{ //省去get方法获取过程 .then((json) => resolve(json)) }) } const getList2 = ()=>{ return new Promise((res,rej) =>{ //省去get方法获取过程 .then((json) => resolve(json)) }) } Promise.all([getList1(),getList2()]).then(value => { //第一个请求的数据 const x = value[0]; //第二个请求的数据 const y = value[1]; //合并操作 for(const i of x){ for(const k of y){ //Todo } } }) 5.2 Promise有哪几种状态,各个状态之间是如何进行转换的?三种状态: pending、fulfilled、rejected (未决定,履行,拒绝)
1.初始化,状态:pending
2.当调用resolve(成功),状态:pengding=>fulfilled
3.当调用reject(失败),状态:pending=>rejected
5.3 Promise 解决哪些问题?回调地狱
const request = url => { return new Promise((resolve,reject) => { $.get(url,params => { resolve(params) }) }) } request(url).then(params1 => { return request(params1.url) }).then(params2 => { return request(params2.url) }).then(params3 => { console.log(params3) }).catch(err => throw new Error(err)) 5.4 Promise.all、Promise.any、Promise.race、Promise.allsettledPromise.all
场景: 多个 Promise 请求, 如果只有一个出错的话, 那么整个就会抛出异常, 不会继续执行
// 模拟异步操作 const request = (delay, flag = true) => { return new Promise((resolve, reject) => { setTimeout(() => { if (flag) { resolve(`成功了${delay}`) } else { reject(`失败了${delay}`) } }, delay) }) } const fun = async (promises) => { Promise.all(promises) .then(res => { console.log('res', res) }) .catch(error => { console.log('error', error) }) } fun([request(1000), request(500)]) // res [ '成功了1000', '成功了500' ] fun([request(1000), request(500, false)]) // error 失败了500如果其中一个错误, 让成功的也能输出出来
const fun = async (promises) => { Promise.all( promises.map(promise => { console.log(promise.catch(err => err)) return promise.catch(err => err) }) ).then(res => { console.log('res', res) }) } fun([request(1000), request(500, false)]) // res [ '成功了1000', '失败了500' ]使用 ES2020 (ES11) 的新语法 Promise.allSettled, 就能捕获正常和错误的返回
const fun = async (promises) => { Promise.allSettled(promises) .then(res => { console.log('res', res) }) } fun([request(1000), request(500, false)]) // res [ // { status: 'fulfilled', value: '成功了1000' }, // { status: 'rejected', reason: '失败了500' } // ] 6. async awaitawait 通常是添加一个 promise 函数嘛
那它可以添加一个普通函数吗,能正确执行吗?
可以添加一个普通函数
那可以添加一个值吗?
可以的,直接返回那个值
为什么 await 后面可以普通函数,或者值?
因为await 后面跟的是一个 Promise 对象,如果不是,则会包裹一层 Promise.resolve()
语法规则
async 是 function 的一个前缀,只有 async 函数中才能使用 await 语法async 函数是一个 Promise 对象,有无 resolve 取决于有无在函数中 return 值await 后面跟的是一个 Promise 对象,如果不是,则会包裹一层 Promise.resolve()async await 原理
async/await 是由 generator函数(迭代器) 来实现的
async await 如何捕获异常
try catch async function fetchData() { try { const result = await fetch('...') } catch (err) { console.log(err) } } fetchData() await 的 catchawait 返回一个 Promise 对象,Promise 对象有 then、catch,我们可以在 catch 中捕获错误
fetchData().then().catch(err => console.log('发生错误:', err)) 6.1 async/await 解决了什么问题?解决了 异步问题, 可以 异步转同步
// 使用async/await获取成功的结果 // 定义一个异步函数,3秒后才能获取到值(类似操作数据库) function getSomeThing(){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('获取成功') },3000) }) } async function test(){ let a = await getSomeThing() console.log(a) } test() // 3秒后输出:获取成功 7. 事件循环 什么是事件循环?js 是单线程, 而为了解决这个问题, 引入了异步, 同步任务会进入主线程执行, 而异步任务会进入任务队列, 等到主线程任务执行完, 任务队列的任务就会放入主线程执行, 如此循环反复就是事件循环。
事件循环中的异步任务?有哪些能举例吗?异步任务可以分为微任务、宏任务
宏任务:定时器、请求、事件监听 (发布订阅 postMessage)、I/O
微任务:promise、async/await
宏任务与微任务那个先执行?在同一次循环中,微任务先执行,宏任务后执行
网上一些面试题,有些执行结果是 宏任务先给出结果,但其内部的微任务仍然会在该宏任务完成之前被优先执行
Vue的$nextTick方法是微任务还是宏任务?
$nextTick在Vue中的实现通常利用 Promise 的then方法来创建一个微任务
Vue 中 watch 与 computer 哪个是同步、哪个是异步?
computer 是同步,watch 是异步
watch 与 computer 哪个先执行?
需要根据实际情况决定,正常情况下是 computer 先执行
如果 watch 设置了 immediate: true ,watch 先于 computer 执行
javaScript-系统知识点【同步和异步】由讯客互联软件开发栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“javaScript-系统知识点【同步和异步】”