面试常考:Promise的用法,以及fetch的原理

63 篇文章 0 订阅
1 篇文章 0 订阅

在了解Promise之前,我们来看一段代码

 

js

复制代码

function foo(){ setTimeout(()=>{ console.log('1') },1000) } function bar(){ console.log('2') } foo() bar

看完这段代码之后,按照传统的,代码从上往下执行,很多人会以为先打印出1,然后再打印出2,但是事实真是这样吗?

事实上,结果恰恰相反,事实上,结果是先打印2,后打印1

我们要知道,js,是单线程执行的,当在调用一个需要过一段时间才执行(像定时器,ajax)的函数时, 会先挂在那里不执行,先执行不耗时间的函数。

上面那段代码,实际上,时先调用bar(),然后再调用foo(),所以是先打印2,后打印1

那么,有没有一种办法,先执行foo(),后执行bar()呢?

答案是肯定的,下面我们来介绍一下Promise

Promise简单介绍

Promise是一个构造函数,用来生成Promise实例。实例代表一个异步操作的最终完成(或失败)及其结果值。

  • Promise有三种状态:

Resolved(已完成) :成功状态,表示操作成功完成。

Rejected(已失败) :失败状态,表示操作失败。

Pending(等待中) :初始状态,既没有被成功(resolved)也没有被拒绝(rejected)。

那么,怎么用Promise来解决上面那个问题呢?

再这里,我们用到的是return new Promise((resolve, reject) => { ... })

return new Promise((resolve, reject) => { ... }) 是JavaScript用来启动一个异步任务并处理其结果的方式。

  • Promise 是一个容器,它会容纳未来可能产生的结果(成功或失败)。

  • 当你创建一个 Promise,你需要给它一个函数(我们称之为 executor 函数),这个函数有两个参数:

    • resolve 是一个函数,当你想说“任务成功了,这里是结果”时,就调用它。
    • reject 也是个函数,如果任务出错了,就调用它来报告错误。
 

js

复制代码

function a(){ return new Promise((resolve,reject)=>{ setTimeout(() => { console.log(1) //怎么让1先执行呢 resolve('xq sucess') }, 2000) }) } function marry(){ return new Promise((resolve, reject)=>{ setTimeout(() => { console.log(2) }, 1000) }) } a() .then((res1)=>{ console.log(res1) return marry()

函数a() :模拟一个耗时2秒的异步操作,

函数marry() :模拟一个耗时1秒的异步操作,

看一下执行的结果

微信图片_20240530143018.png

结果是我们想要的:12之前打印

为什么是这样的顺序?

  • 1.首先,a()函数被调用,它返回一个Promise并在2秒后打印数字1,随后调用resolve('xq success'),将Promise状态变为fulfilled。

  • 2.这时,a().then被触发,打印出a()解决时传递的字符串'xq success'

  • 3.接下来,在.then中调用marry()函数,它同样返回一个Promise并在1秒后打印数字2。但是,请注意,在marry()的Promise中,虽然执行了resolve函数来改变Promise状态为fulfilled,但实际上并没有提供任何参数给resolve函数(根据您的代码显示),因此在marry()的链式调用中没有额外的值被打印出来。

打印结果将是数字1,字符串'xq success',然后是数字2

问题来了,.then后面的参数res1是指什么

res1就是a()resolve函数传递的参数,即字符串'xq success'

下面来看一段复杂的代码

 

js

复制代码

function a() { return new Promise((resolve, reject) => { setTimeout(() => { console.log(1) //怎么让1先执行呢 resolve('xq sucess') }, 2000) }) } function marry() { return new Promise((resolve, reject) => { setTimeout(() => { console.log(2) resolve('happy') }, 1000) }) } function baby() { } a() .then((res1) => { console.log(res1) return marry() }).then((res2) => { console.log(res2) baby() })

相信看见这段代码,按照之前的解释,你应该知道最后输出的顺序了吧!

微信截图_20240530145237.png

1 a()函数在2秒后打印1,并使用resolve方法将Promise状态改为fulfilled,传递字符串'xq success'

2 这导致第一个.then中的回调函数执行,打印res1,即'xq success',然后返回marry()函数的Promise。

3 marry()函数的Promise在1秒后打印2,并使用resolve方法传递字符串'happy',使得它的Promise变为fulfilled。

4 这又触发了紧跟的第二个.then,打印出res2,即'happy',并调用baby()函数。

  • 由于baby()函数体内没有任何实现,所以它被执行但没有额外的输出或可见效果。 因此,整体的输出顺序为:1 -> 'xq success' -> 2 -> 'happy',然后baby()被调用但无输出。

问题来了,第二个.then后面的参数res2是指什么

第二个.then后面的参数res2是指由前一个Promise(即marry()函数返回的Promise)通过resolve方法传递的值,即字符串'happy'

下面,我们来讲一下js当中,fectch是如何通过Promise将ajax进行封装的

 

js

复制代码

function getData() { let xhr = new XMLHttpRequest(); xhr.open('GET', 'https://mock.mengxuegu.com/mock/65a91543c4cd67421b34c898/movie/movieList', true); xhr.send() xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { let movieList = JSON.parse(xhr.responseText).movieList; console.log(movieList) //console.log(xhr.responseText); resolve(movieList) } } } function renderLi(arr) { //创建li arr.forEach(item => { let li = document.createElement('li') li.innerHTML = item.nm document.getElementById('ul').appendChild(li); }); } document.getElementById('btn').addEventListener('click', () => { getData().then(res => { renderLi(res) }) })

这些是之前我们通过ajax,向后端发送请求,获取电影列表的代码

在这里getData()叫做异步代码(需要消耗时),renderLi叫做同步代码,我们必须异步拿到数据之后,再执行同步代码

所以我们需要在getData()函数里return new Promise((resolve, reject) => { ... })

 

js

复制代码

function getData() { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open('GET', 'https://mock.mengxuegu.com/mock/65a91543c4cd67421b34c898/movie/movieList', true); xhr.send() xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { let movieList = JSON.parse(xhr.responseText).movieList; console.log(movieList) //console.log(xhr.responseText); resolve(movieList) } } }) } function renderLi(arr) { //创建li arr.forEach(item => { let li = document.createElement('li') li.innerHTML = item.nm document.getElementById('ul').appendChild(li); }); } document.getElementById('btn').addEventListener('click', () => { getData().then(res => { renderLi(res) }) })

这样,我们在执行renderLi函数之前,执行了getData()

在这里,你发现了什么?

我们是不是可以把url地址变成getData()的一个参数,是不是就完成了一个封装的操作

微信截图_20240530152334.png

 

js

复制代码

function getData(url) { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.send() xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { let movieList = JSON.parse(xhr.responseText).movieList; console.log(movieList) //console.log(xhr.responseText); resolve(movieList) } } }) function renderLi(arr) { //创建li arr.forEach(item => { let li = document.createElement('li') li.innerHTML = item.nm document.getElementById('ul').appendChild(li); }); } document.getElementById('btn').addEventListener('click', () => { getData('https://mock.mengxuegu.com/mock/65a91543c4cd67421b34c898/movie/movieList') .then(res => { return res.JSON() }).then(data =>{ console.log(data) }) }) }

观察一下,这里的getData()是不是就是fetch

此时此刻的你

微信截图_20240530153215.png

原来fetch是这么来的,这下恍然大悟了

那么Promise里reject是什么呢

reject是用来捕获错误,当在调用一个需要过一段时间才执行(像定时器,ajax)的函数时,像发送请求可能失败,这时候需要.catch

总结一下

1.then((res) =>{}) res是promise中的resolve(xx)出来的值

2.catch((res) =>{}) err是promise中的reject(xx)出来的值


原文链接:https://juejin.cn/post/7374661722466123830
 


 

  • 38
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值