目录
JS异步编程 (2) - Promise、Generator、async/await
Promise
Generator
Async/Await
首页 web前端 js教程 JS异步编程之Promise、Generator、async/await

JS异步编程之Promise、Generator、async/await

Jul 07, 2018 am 09:56 AM

 这篇文章主要介绍了关于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异步编程的介绍

React-Reflux的基础介绍

以上是JS异步编程之Promise、Generator、async/await的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前 By 尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

在JavaScript中替换字符串字符 在JavaScript中替换字符串字符 Mar 11, 2025 am 12:07 AM

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

自定义Google搜索API设置教程 自定义Google搜索API设置教程 Mar 04, 2025 am 01:06 AM

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

构建您自己的Ajax Web应用程序 构建您自己的Ajax Web应用程序 Mar 09, 2025 am 12:11 AM

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

示例颜色json文件 示例颜色json文件 Mar 03, 2025 am 12:35 AM

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

10个jQuery语法荧光笔 10个jQuery语法荧光笔 Mar 02, 2025 am 12:32 AM

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

8令人惊叹的jQuery页面布局插件 8令人惊叹的jQuery页面布局插件 Mar 06, 2025 am 12:48 AM

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

10 JavaScript和JQuery MVC教程 10 JavaScript和JQuery MVC教程 Mar 02, 2025 am 01:16 AM

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

什么是这个'在JavaScript? 什么是这个'在JavaScript? Mar 04, 2025 am 01:15 AM

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

See all articles