IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 基于Promise 自己实现一个 asyncawait。从此异步编程变得更简单! -> 正文阅读

[JavaScript知识库]基于Promise 自己实现一个 asyncawait。从此异步编程变得更简单!

碎碎念:

亲爱的读者:你好!我叫 Changlon —— 一个非科班程序员、一个致力于前端的开发者、一个热爱生活且又时有忧郁的思考者。

如果我的文章能给你带来一些收获,你的点赞收藏将是对我莫大的鼓励!

我的邮箱:thinker_changlon@163.com

我的Github: https://github.com/Changlon

参考文章:
Generator 函数的含义与用法
MDN Generator

github源码地址:myasync

自从有了Promise这个好用的技术,我们的异步编程变得更加简洁明了,告别了那个回调地狱的时代。我们只需要不停得调用then函数就能清爽地组织下一个事件循环中的代码!
那么这样就够了吗?我们能否再进一步,把异步编程变得像同步编程那样?
答案是肯定的。
举个例子:

	import {queryData} from '/api' 
	methods:{
	 setData() {
			const that = this 
			queryData()		
				.then(res=>{
					that.data = res.data 
				})
			}
	}	

在上面这个例子中,我们在一个同步函数setData中调用了 queryData请求后端数据,queryData返回一个Promise,我们在then方法中编写了获取到数据后要调用的方法,即把获取到的数据设置到that.data中去。
这样一看似乎是没有什么问题,但是如果当业务逻辑不断变更,代码量不断增多后,需要处理很多这样的then方法中的回调有时候修改某个地方的代码就有可能因漏掉一个大括号而寻找半天!可以看出光有Promise这种异步编程方式还是有一点欠缺的!
因此就诞生了异步函数! 还是上面那个例子我们看一下异步函数是如何写的。

import {queryData} from '/api' 
	methods:{
	async setData() {
			const result = await queryData() 
			if(result && result.data) this.data = result.data		
	}	

可以很明显得看出异步函数编写的优势,以一种同步的代码书写方式我们就可以写出异步回调执行的代码!上面的代码在执行到

const result = await queryData() 

这一句时就会进入等待模式,当queryData函数成功返回数据后再接着往下执行后面的代码!

那么本章我们的任务是通过手写一个async_函数来了解async ,await的执行原理!为什么加了一个await就能实现一个类似阻塞的效果!

在了解原理之前我们要先来补充一个知识点叫做: Generator生成器
如果已经有了解的同学就请直接跳过。

首先看一个实例:

//定义Generator生成器
	function * gen() {
		yield 1
		yield 2 
		yield 3
	}	
	
	let  g =  gen()  
	let result = g.next()
	while(!result.done) {
		console.log(result.value) 
		result = g.next()
	}
	

上面的代码会打印出// 1,2,3
当我们的函数以function * 形式定义的时候,它就是一个函数生成器。当我们执行这个函数生成器后可以得到一个可以操作函数执行迭代的对象。可以简单的理解为函数的执行器。它的原型上有一个非常重要的方法:Generator.prototype.next() 作用是返回一个由 yield表达式生成的值。
返回的数据格式是:{value,done} valueyield后面的值,done是表示该函数迭代器是否执行到底。
所以上面的例子通过done判断是否执行到生成器的底部,通过value打印出每个yield后面的值。所以就打印了 // 1,2,3

其实next函数我们还可以给它传递一个参数值 如g.next(1),这个参数值在函数迭代器g执行时赋给yield表达式前面的变量。
如下代码:

function * gen() {
    console.log(1)
    let v =   yield   
    console.log(v)

    console.log(2)
    yield  
    console.log(3)
    yield 
}


let g = gen() 


 console.log(g.next('a')) 
 console.log(g.next('a')) 

最终打印的结果为: 1,{value: undefined, done: false},a,2,{value: undefined, done: false}
为什么会出现上面这样的结果呢?我们来简单分析一下:
1.首先打印执行的是 console.log(g.next('a')),先会执行第一个yield前的所有语句代码 console.log(1) //打印出1,之后把 'a'赋给 v,最后返回yield后的信息{value: undefined, done: false}并打印,所以第一个console打印的是:

1,{value: undefined, done: false}

  1. 接着执行第二个console.log,首先是g.next('a') 它会执行第二个yield之前,第一个yield之后的代码即打印v和2,由于v之前被赋值为 'a'所以第二个console打印的数据是:

a,2,{value: undefined, done: false}

综上就解释了为何会出现这样的打印数据,我们也基本理解的生成器的用法,简单来说就是通过代码中的yield来控制迭代函数的执行!
有了上面的铺垫我们就可以来实现我们自己的async,await了。

首先给出我们自己的async_函数的异步使用方法,然后我们自己去编写async_的实现!

import {async_,promise} from "../index.js"  

async_(function *() {

    console.log(`测试async_ ... `) 
    let a = 1 
    let b = 2 

    let c =yield new promise((resolve,reject)=>{ 
        setTimeout(()=>{
            resolve(a+b)
        },1000) 
    })

    console.log(`计算结果a+b = ${c}`) 

    console.log(`请求后台数据...`) 

    let result = yield new promise((resolve,reject)=>{ 
            let r =  Math.floor(Math.random()*10)  
            if(r%2==0) {
                resolve({
                    code:200,
                    msg:'请求成功!',
                    data:[
                        {name:'changlon',age:21},
                        {name:'jack',age:25}
                    ]
                })
            }else{
                reject(new Error('网络请求失败!'))
            }
    })

    if(result && result.code==200) {
        console.log(`数据请求成功:${result.data}`)
    }else{
        console.log(`数据请求失败:${result}`)
    }

    console.log(`测试 async_ 完毕。`)

})

要想让上面的这段代码已异步的方式去执行我们首先得定义一个 async_函数,这个函数接受一个 生成器函数Generator作为参数。


function async_(generator) {     

}

好的,我们的函数就定义好了。下面的重点是如何运用Generator生成器迭代执行异步方法。
我们的大概思路就是: 每次调用next函数返回的value值作为参数传递给Promise.resolve方法,在then函数中将返回的值作为下一次 next迭代函数的参数传入。这样递归的执行下去,直至结束。

下面给出实现方法:

function async_(generator) {     

    const isValid = generator && "[object Generator]" === Object.prototype.toString
   .call(generator.prototype)   
   
   if(!isValid) throw new Error(`async_ typeError: 请传入生成器函数!错误类型 => ${generator}`) 
   const gen =  generator() 
   excutor( gen ,gen.next() ) 
   function excutor(gen,result) {    
       if(result.done) return void 0 
       promise.resolve(result.value).then(val=>{  
          excutor(gen,gen.next(val))
       })
       .catch(err=>{
           excutor(gen,gen.next(err))
       })   
   }
}

需要注意的是我们上面的Promise是使用的上一次我们自己封装的一个promise,这样我们大概就实现了自己的async,await。浏览器中的async,await原理也是相同的。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-10-26 12:07:25  更:2021-10-26 12:08:59 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/13 7:11:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码