第5章 JS基础-异步【三座大山之三,必考!!!】

返回章节目录

目录

1.同步和异步的区别

单线程和异步

为什么需要异步

2.应用场景

3.promise

4.练习题

1.手写用promise加载一张图片

2.setTimeout笔试题


1.同步和异步的区别

单线程和异步

1.JS是单线程语言,只能同时做一件事

2.浏览器和nodejs已支持JS启动进程,比如Web Worker

3.JS和DOM渲染共用同一个线程,因为JS可修改DOM结构

DOM渲染时,JS执行必须停止,JS执行时,DOM渲染也必须停止

为什么需要异步

遇到等待(网络请求,定时任务)不能卡住,总不能去等待网络请求然后浏览器卡在这里吧????

等待的过程cpu也是空闲的,这样也会浪费资源

异步回调都是用callback的函数形式

异步:

同步:

不点击alert就会阻塞卡住,3不会打印

异步不会阻塞代码执行,同步会阻塞代码执行

2.应用场景

异步主要用在等待的情况(网络请求、定时任务

先打印start,再异步执行网络请求,接着打印end,网络请求什么时候回来就什么时候打印data1的数据

先打印start,接着定义image,再加上onload事件,什么时候加载完就什么时候执行回调,这也是回调的另一种形式。

接着执行img.src赋值,这里一执行就开始加载图片,然后打印end,最后图片加载完的时候执行回调打印loaded

3.promise

早期JS会陷入回调地狱(callback hell),获取第一份数据之后再获取第二份数据,获取到第二份数据后再获取第三份数据...这样的嵌套回调太多了就特别乱,就像地狱一般

嵌套的回调地狱的问题导致了promise的出现,也就是说promise的出现,解决了callback嵌套的回调地狱(callback hell)问题

promise的then都是并列的管道形式,当然这里也是回调,但是不是嵌套的更方便调试和查看

具体promise的例子我们后面在说,这里看一下形式

4.练习题

1.手写用promise加载一张图片

function loadImg(src) {
    const p = 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
        }
    )
    return p
}

// const url = 'https://img.mukewang.com/5a9fc8070001a82402060220-140-140.jpg'
// loadImg(url).then(img => {
//     console.log(img.width)
//     return img
// }).then(img => {
//     console.log(img.height)
// }).catch(ex => console.error(ex))

const url1 = 'https://img.mukewang.com/5a9fc8070001a82402060220-140-140.jpg'
const url2 = 'https://img3.mukewang.com/5a9fc8070001a82402060220-100-100.jpg'

loadImg(url1).then(img1 => {
    console.log(img1)
    return img1 // 普通对象
}).then(img1 => {
    console.log(img1)
    return loadImg(url2) // promise 实例
}).then(img2 => {
    console.log(img2)
    return img2
}).then(img2 => {
    console.log(img2)
}).catch(ex => console.error(ex))

运行结果:

深入细节:当一个 Promise 完成(fulfilled)或者失败(rejected)时,返回函数将被异步调用(由当前的线程循环来调度完成)。具体的返回值依据以下规则返回。如果 then 中的回调函数
①返回了一个值,那么 then 返回的 Promise 将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。

       执行then之后是会返回Promise对象的,比如这里回调函数返回img1,然后将返回值img1作为参数传给下一个then的参数,这里PromiseValue是img,因为loadImg(url1)会执行resolve(img),resolve函数返回promise对象,这个img就是上面的img1,这是第一个url1加载出来的img


②没有返回任何值,那么 then 返回的 Promise 将会成为接受状态,并且该接受状态的回调函数的参数值为 undefined。

       比如下图,没有返回值的Promise对象的PromiseValue就是undefined


③抛出一个错误,那么 then 返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。

       这里的③和⑤我准备一起说,所以要看例子请直接看到⑤的例子


④返回一个已经是接受状态的 Promise,那么 then 返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。

       这里因为返回接受状态的promise,所以传给下一个then就是img,因为执行了resolve(img),所以resolve函数返回的promise对象包含img,这是第二个url2加载出来的img,最后打印<img>标签也和上面2个不同


⑤返回一个已经是拒绝状态的 Promise,那么 then 返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。

       请求失败的情况,并且最后没有catch的情况下,PromiseStatus为rejected

这里到then里面的回调函数return loadImg(....)的时候就是返回已经是拒绝状态的Promise,即执行了reject(err)返回的Promise,那么then返回的Promise也是拒绝状态,往后传参数的的时候,传的err,下一个then的参数img2就是err(就是③中的将抛出的错误作为拒绝状态Promise的回调函数的参数值),所以这里PromiseValue是Error: 图片加载失败 https://img3.mukewang123123.com/5a9fc8070001a82402060220-100-100.jpg at HTMLImageElement.img.onerror (<anonymous>:9:29)

这里返回的Promise的状态居然是resolved,error都打印了为什么还是resolved呢?因为我们catch了,捕捉了错误,所以状态执行正确,没问题,如果去掉catch,那么最后的拒绝状态的Promise没有被捕捉处理,PromiseStatus就是rejected


⑥返回一个未定状态(pending)的 Promise,那么 then 返回 Promise 的状态也是未定的,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。

第一次请求不到则超时,PromiseStatus为pending,最后加不加catch只是影响能不能捕捉到拒绝状态Promise的问题,这里不会影响Promise的pending状态

2.setTimeout笔试题

运行结果

先打印1354,1s后打印2

更复杂的题目Promise和setTimeout混合的涉及到事件循环,大家可以看我另一篇博客:

用Promise的例子解释事件循环EventLoop

关注、留言,我们一起学习。

===============Talk is cheap, show me the code================

发布了224 篇原创文章 · 获赞 983 · 访问量 91万+

猜你喜欢

转载自blog.csdn.net/qq_34115899/article/details/104113751