李恒道 发表于 2021-8-23 01:29:27

[油猴脚本开发指南]包装异步代码为同步代码

# 前言

这节课我们学习如何将一段异步代码包装成同步代码

# 异步代码

```javascript
function GetMess(){
      GM_xmlhttpRequest({
      url:"https://bbs.tampermonkey.net.cn/",
      method :"GET",
      headers: {
      },
      onload:function(xhr){
            console.log(xhr.responseText);
      }
    });
}
GetMess()
```

这是一段十分简单的代码,在函数内使用了一个get获取网页内容,如果我们想在获取内容之后进行一些处理需要写在onload函数内,这种代码就叫做异步代码,当执行完请求后再回调处理的函数,有时候这样非常麻烦,那有没有什么办法改为同步代码呢?解决方案就是使用Promise

```javascript
    let p=new Promise((resolve, reject) => {

    })
```

我们首先创建一个Promise,当Promise不调用resolve和reject结束状态,那将一直是pengding状态,只有调用resolve或reject的时候,才可以结束状态并返回resolve或reject函数内的值

```javascript
function GetMess(){
    let p=new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
            url:"https://bbs.tampermonkey.net.cn/",
            method :"GET",
            headers: {
            },
            onload:function(xhr){
                resolve(xhr.responseText)

            }
      });
    })
}
GetMess()
```

现在我们要求在onload后使用resolve结束这个Promise状态,并返回xhr.responseText的值

我们再结合async和await的语法糖就可以了

await需要一个Promise,所以我们需要将我们构建的这个Promise返回

```javascript
function GetMess(){
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
            url:"https://bbs.tampermonkey.net.cn/",
            method :"GET",
            headers: {
            },
            onload:function(xhr){
                resolve(xhr.responseText)

            }
      });
    })
}
async function GetPromiseAndWait(){
   let text=await GetMess()
}
GetPromiseAndWait()
```

添加await GetMess()后,GetMess函数会返回一个Promise,Async和Await语法糖会使Promise阻塞直至完成状态后,将resolve内填入的值赋值给text变量上。

需要注意的是我们不可以在全局作用域内使用await,目前并未实现该特性,只有在async的函数内才可以使用await。

由于GetPromiseAndWait使用了Async语法糖,所以调用他现在也会返回一个Promise变量,如果我们想要同步获取他返回的值,也需要使用Async与Await

```javascript
function GetMess(){
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
            url:"https://bbs.tampermonkey.net.cn/",
            method :"GET",
            headers: {
            },
            onload:function(xhr){
                resolve(xhr.responseText)

            }
      });
    })
}
async function GetPromiseAndWait(){
   let text=await GetMess()
   return '我处理完了'
}
function WantGetFinallyText(){
    let data=GetPromiseAndWait()
    console.log('WantGetFinallyText',data)
}

WantGetFinallyText()
```

![图片.png](data/attachment/forum/202108/23/012142byxz5d8dxdoopff0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "图片.png")

我们可以看到console.log输出了一个promise,这个值是pengding,因为我们调用了GetPromiseAndWait,他在没有执行到return的时候是一个执行中的状态。

如果我们想要获取到GetPromiseAndWait的值,需要让这个函数执行到return,也就是fulfilled状态。这个时候我们需要对调用这个函数返回的Promise再进行一个Async和Await,来让这个Promise形成阻塞

```javascript
function GetMess(){
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
            url:"https://bbs.tampermonkey.net.cn/",
            method :"GET",
            headers: {
            },
            onload:function(xhr){
                resolve(xhr.responseText)

            }
      });
    })
}
async function GetPromiseAndWait(){
   let text=await GetMess()
   return '我处理完了'
}
async function WantGetFinallyText(){
    let data=await GetPromiseAndWait()
    console.log('WantGetFinallyText',data)
}

WantGetFinallyText()
```

![图片.png](data/attachment/forum/202108/23/012358yigfcl2ni7nbl37g.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "图片.png")

这个时候await就会阻塞住GetPromiseAndWait一直到履行状态再将其值赋值给data,因为return会使函数变为履行状态,也就是说await会使函数一直阻塞到return返回内容为止。

注意,还有一个知识点

就是GetPromiseAndWait如果return一个Promise,那这个函数返回的将是这个Promise,await阻塞的也是这个Promise的值。

这个概念我们之前也讲过,当async函数return一个值时,会将其包装成一个Promise,当返回一个Promise时,将直接返回这个Promise

那么到这里,你就学会了Promise以及多层Async函数的同步!

# 结语

撒花~

syy 发表于 2021-8-23 07:43:34

赶上热乎的了 趁机占个楼

Ne-21 发表于 2021-8-23 08:10:05

太好了{:4_94:}

陈公子的话 发表于 2021-8-23 10:14:22

哥哥最近有点高产啊

李恒道 发表于 2021-8-23 10:37:45

syy 发表于 2021-8-23 07:43
赶上热乎的了 趁机占个楼

谢谢哥哥

李恒道 发表于 2021-8-23 10:37:50

Ne-21 发表于 2021-8-23 08:10
太好了

谢谢哥哥

李恒道 发表于 2021-8-23 10:38:02

小陈 发表于 2021-8-23 10:14
哥哥最近有点高产啊

闲着没事想先写几篇,哥哥要不要也写点玩玩

陆游棋 发表于 2022-10-19 16:06:14

写的很好,受益良多

jinyu2 发表于 2022-11-30 23:53:12

本帖最后由 jinyu2 于 2022-12-1 00:13 编辑

补充一点,如果请求返回404等错误,可以这么写
// GM_http
onload:function(xhr){
                if (xhr.status == 200) {
                  resolve(xhr.responseText);
                }else {reject(xhr.status);}
            },
//省略不必要的代码
try {
      var text = await GetMess();
      //这里是返回reslve的值
    } catch (err) {
      //返回reject
      console.log('我发生了错误',err)


李恒道 发表于 2022-12-1 00:03:27

jinyu2 发表于 2022-11-30 23:53
有一个问题,对于失败的请求,我要返回错误码。我的写法是

但是这样并不能区分resolve和reject。我该如何 ...

reject触发了异常
直接在函数里中断了
应该改成resolve
本质上promise就是相当于契约
你应该自己制定契约的同意和错误的规则
而不是根据网络请求指定的直接往上传
页: [1] 2
查看完整版本: [油猴脚本开发指南]包装异步代码为同步代码