JS异步编程之Promise、Generator、async/await
这篇文章主要介绍了关于JS异步编程之Promise、Generator、async/await ,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下
JS异步编程 (2) - Promise、Generator、async/await
上篇文章我们讲了下JS异步编程的相关知识,比如什么是异步,为什么要使用异步编程以及在浏览器中JS如何实现异步的。
最后我们捎带讲了几种JS异步编程模式(回调,事件和发布/订阅模式),这篇我们继续去深入了解下其他的几种异步编程模式。
Promise
Promise是ES6推出的一种异步编程的解决方案。其实在ES6之前,很多异步的工具库就已经实现了各种类似的解决方案,而ES6将其写进了语言标准,统一了用法。Promise解决了回调等解决方案嵌套的问题并且使代码更加易读,有种在写同步方法的既视感。
我们先来简单了解下ES6中Promise的用法
var p = new Promise(function async(resolve, reject){ // 这里是你的异步操作 setTimeout(function(){ if(true){ resolve(val); }else{ reject(error); } }, 1000) }) p.then(function(val){ console.log('resolve'); }, function(){ console.log('reject'); })
首先,ES6规定Promise是个构造函数,其接受一个函数作为参数如上面代码中的async
函数,此函数有两个参数,resolve、reject分别对应成功失败两种状态,我们可以选择在不同时候执行resolve或者reject去触发下一个动作,执行then方法里的函数。
我们可以简单对比下回调的写法和promise的写法的不同
对于传统回调写法来说,一般会写成这样
asyncFn1(function () { asyncFn2(function() { asyncFn3(function() { // xxxxx }); }); });
或者我们将各个回调函数拆出来独立来写以减少耦合,像是这样:
function asyncFn1(callback) { return function() { console.log('asyncFn1 run'); setTimeout(function(){ callback(); }, 1000); } }function asyncFn2(callback) { return function(){ console.log('asyncFn2 run'); setTimeout(function(){ callback(); }, 1000); } }function normalFn3() { console.log('normalFn3 run'); } asyncFn1(asyncFn2(normalFn3))()
最后我们看下Promise的写法
function asyncFn1() { console.log('asyncFn1 run'); return new Promise(function(resolve, reject) { setTimeout(function(){ resolve(); }, 1000) }) }function asyncFn2() { console.log('asyncFn2 run'); return new Promise(function(resolve, reject) { setTimeout(function(){ resolve(); }, 1000) }) }function normalFn3() { console.log('normalFn3 run'); } asyncFn1().then(asyncFn2).then(normalFn3);
这样来看无论是第一种还是第二种写法,都会让人感到不是很直观,而Promise的写法更加直观和语义化。
Generator
Generator函数也是ES6提供的一种特殊的函数,其语法行为与传统函数完全不同。
我们先直观看个Generator实际的用法
function* oneGenerator() { yield 'Learn'; yield 'In'; return 'Pro'; }var g = oneGenerator(); g.next(); // {value: "Learn", done: false}g.next(); // {value: "In", done: false}g.next(); // {value: "Pro", done: true}
Generator函数是一种特殊的函数,他有这么几个特点:
声明时需要在
function
后面加上*
,并且配合函数里面yield
关键字来使用。在执行Generator函数的时候,其会返回一个Iterator遍历器对象,通过其next方法,将Generator函数体内的代码以yield为界分步执行
具体来说当执行Generator函数时,函数并不会执行,而是需要调用Iterator遍历器对象的next方法,这时程序才会执行
从头或者上一个yield之后
到到下一个yield或者return或者函数体尾部
之间的代码,并且将yield后面的值,包装成json对象返回。就像上面的例子中的{value: xxx, done: xxx}
。value取的yield或者return后面的值,否则就是undefined,done的值如果碰到return或者执行完成则返回true,否则返回false。
我们知道了简单的Generator函数的用法以后,我们来看下如何使用Generator函数进行异步编程。
首先我们先来看下使用Generator函数能达到怎样的效果。
// 使用Generator函数进行异步编程function* oneGenerator() { yield asyncFn1(); yield asyncFn2(); yield normalFn3(); }// 我们来对比一下PromiseasyncFn1().then(asyncFn2).then(normalFn3);
我们可以看出使用Generator函数进行异步编程更像是在写同步任务,对比Promise少了很多次then方法的调用。
好,那么接下来我们就来看下如何实际使用Generator函数进行异步编程。
这里我要特别说明一下,事实上Generator函数不像Promise一样是专门用来解决异步处理而产生的,人们只是使用其特性来产出了一套异步的解决方案,所以使用Generator并不像使用Promise一样有一种开箱即用的感觉。其更像是在Promise或者回调这类的解决方案之上又封装了一层,让你可以像上面例子里一样去那么写。
我们还是具体来看下上面的例子,我们知道单写一个Generator是不能运行的对吧,我们需要执行他并且使用next方法来让他分步执行,那么什么时候去调用next呢?答案就是我们需要在异步完成时去调用next。我们来按照这个思路补全上面的例子。
var g;function asyncFn() { setTimeout(function(){ g.next(); }, 1000) }function normalFn() { console.log('normalFn run'); }function* oneGenerator() { yield asyncFn(); return normalFn(); } g = oneGenerator(); g.next();// 这里在我调用next方法的时候执行了asyncFn函数// 然后我们的希望是在异步完成时自动去再调用g.next()来进行下面的操作,所以我们必须在上面asyncFn函数体内的写上g.next(); 这样才能正常运行。// 但其实这样是比较奇怪的,因为当我定义asyncFn的时候其实是不知道oneGenerator执行后叫什么名儿的,即使我们提前约定叫g,但这样asyncFn就太过于耦合了,不仅写法很奇怪而且耦合太大不利于扩展和重用。反正总而言之这种写法很不好。
那么怎么解决呢,我们需要自己写个方法,能自动运行Generator函数,这种方法很简单在社区里有很多,最著名的就是大神TJ写的co模块,有兴趣的同学可以看下其源码实现。这里我们简单造个轮子:
// 如果我们想要去在异步执行完成时自动调用next就需要有一个钩子,回调函数的callback或者Promise的then。function autoGenerator(generator){ var g = generator(); function next(){ var res = g.next(); // {value: xxx, done: xxx} if (res.done) { return res.value; } if(typeof res.value === 'function'){ // 认为是回调 res.value(next); }else if(typeof res.value === 'object' && typeof res.value.then === 'function'){ // 认为是promise res.value.then(function(){ next(); }) }else{ next(); } } next(); }// ----function asyncFn1(){ console.log('asyncFn1'); return new Promise(function(resolve){ setTimeout(function(){ resolve(); }, 1000) }) }function asyncFn2() { console.log('asyncFn2'); return function(callback){ setTimeout(function(){ callback(); }, 1000); } }function normalFn() { console.log('normalFn'); }function* oneGenerator() { yield asyncFn1(); yield asyncFn2(); yield normalFn(); } autoGenerator(oneGenerator);
这个方法我们简单实现了最核心的部分,有些判断可能并不严谨,但大家理解这个思路就可以了。有了这个方法,我们才可以方便的使用Generator函数进行异步编程。
Async/Await
如果你学会了Generator函数,对于Async函数就会很容易上手。你可以简单把Async函数理解成就是Generator函数+执行器。我们就直接上实例好了
function asyncFn1(){ console.log('asyncFn1'); return new Promise(function(resolve){ setTimeout(function(){ resolve('123'); }, 2000) }) }function asyncFn2() { console.log('asyncFn2'); return new Promise(function(resolve){ setTimeout(function(){ resolve('456'); }, 2000) }) } async function asyncFn () { var a = await asyncFn1(); var b = await asyncFn2(); console.log(a,b) } asyncFn();// asyncFn1// asyncFn2// 123,456
当然async里实现的执行器肯定是跟咱们上面简单实现的有所不同,所以在用法上也会有些注意的点
首先async函数的返回值是一个Promise对象,不像是generator函数返回的是Iterator遍历器对象,所以async函数执行后可以继续使用then等方法来继续进行下面的逻辑
await后面一般跟Promise对象,async函数执行时,遇到await后,等待后面的Promise对象的状态从pending变成resolve的后,将resolve的参数返回并自动往下执行直到下一个await或者结束
await后面也可以跟一个async函数进行嵌套使用。
对于异步来说,还有很多的知识点我们没有讲到,比如异常处理,多异步并行执行等等,这篇和上篇文章主要还是希望大家对异步编程有个直观的了解,清楚各种解决方案之间的区别和优劣。由于篇幅和精力有限,对于其他我们没讲到的知识点,如果大家有兴趣有机会我会再写文章深入讲解的。
以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!
相关推荐:
以上是JS异步编程之Promise、Generator、async/await的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

JavaScript字符串替换方法详解及常见问题解答 本文将探讨两种在JavaScript中替换字符串字符的方法:在JavaScript代码内部替换和在网页HTML内部替换。 在JavaScript代码内部替换字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 该方法仅替换第一个匹配项。要替换所有匹配项,需使用正则表达式并添加全局标志g: str = str.replace(/fi

本教程向您展示了如何将自定义的Google搜索API集成到您的博客或网站中,提供了比标准WordPress主题搜索功能更精致的搜索体验。 令人惊讶的是简单!您将能够将搜索限制为Y

因此,在这里,您准备好了解所有称为Ajax的东西。但是,到底是什么? AJAX一词是指用于创建动态,交互式Web内容的一系列宽松的技术。 Ajax一词,最初由Jesse J创造

本文系列在2017年中期进行了最新信息和新示例。 在此JSON示例中,我们将研究如何使用JSON格式将简单值存储在文件中。 使用键值对符号,我们可以存储任何类型的

增强您的代码演示:开发人员的10个语法荧光笔 在您的网站或博客上共享代码片段是开发人员的常见实践。 选择合适的语法荧光笔可以显着提高可读性和视觉吸引力。 t

利用轻松的网页布局:8个基本插件 jQuery大大简化了网页布局。 本文重点介绍了简化该过程的八个功能强大的JQuery插件,对于手动网站创建特别有用

本文介绍了关于JavaScript和JQuery模型视图控制器(MVC)框架的10多个教程的精选选择,非常适合在新的一年中提高您的网络开发技能。 这些教程涵盖了来自Foundatio的一系列主题

核心要点 JavaScript 中的 this 通常指代“拥有”该方法的对象,但具体取决于函数的调用方式。 没有当前对象时,this 指代全局对象。在 Web 浏览器中,它由 window 表示。 调用函数时,this 保持全局对象;但调用对象构造函数或其任何方法时,this 指代对象的实例。 可以使用 call()、apply() 和 bind() 等方法更改 this 的上下文。这些方法使用给定的 this 值和参数调用函数。 JavaScript 是一门优秀的编程语言。几年前,这句话可
