基于javascript的异步编程实例详解

本文实例讲述了基于javascript的异步编程。分享给大家供大家参考,具体如下:

异步函数这个术语有点名不副实,调用一个函数后,程序只在该函数返回后才能继续。JavaScript程序员如果称一个函数为异步的,其意思就是

函数会导致将来再运行另一个函数,后者取自于事件队列

。如果后面这个函数是作为参数传递给前者的,则称其为回调函数

callback

回调函数是异步编程最基本的方式。

采用这种方式,我们把同步操作变成了异步操作,主函数不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。

回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护。

我们定义一个delay函数,它是异步的,也就是说它会拖延指定函数的执行,从而使现在正在执行的程序继续执行。delay函数如下:

rush:js;"> function delay(time,callback) { setTimeout(function () { callback("Slept for "+time); },time); }

那么如果我要delay两次呢?

"Slept for 1000" //...waits another 1200ms // > "Slept for 1200"

只有这样我们才能够确保两个delay一个一个的执行。如果层次多了呢?就会形成回调地狱。当异步任务很多时,维护大量的callback将是一场灾难。

Promise

Promise是一个被纳入ES6中的规范,各大框架也早已实现相关方法

Promise可以理解为一个承诺,如果A调用B,B返回一个承诺给A,然后A就可以在写计划的时候这么写,当B返回结果的时候,A就执行方案1,如果B没有返回A要的结果,A就执行方案2。这样一来,

我们看看ES6中promise的用法示例:

rush:js;"> 'use strict'; var promiseCount = 0; function testPromise() { var thisPromiseCount = ++promiseCount; var log = document.getElementById('log'); log.insertAdjacentHTML('beforeend',thisPromiseCount +') Started (Sync code started)
'); // 我们创建一个新的Promise,期望3秒后得到结果 var p1 = new Promise( //当Promise解决或拒绝时该函数调用 function(resolve,reject) { log.insertAdjacentHTML('beforeend',thisPromiseCount +') Promise started (Async code started)
'); // 创建异步操作 window.setTimeout( function() { // 满足Promise resolve(thisPromiseCount); },Math.random() * 2000 + 1000); }); // 当Promise被满足时执行 p1.then( // 输出信息和值 function(val) { log.insertAdjacentHTML('beforeend',val +') Promise fulfilled (Async code terminated)
'); }) .catch( // 当Promise被拒绝时执行 function(reason) { console.log('Handle rejected promise ('+reason+') here.'); }); log.insertAdjacentHTML('beforeend',thisPromiseCount +') Promise made (Sync code terminated)
'); }

快速连续执行函数,得到结果:

说明1,2异步操作后正常顺序执行完毕。更多Promise的详细用法请参考:

很多框架也提供了Promise相关方法,这里我们以jQuery为例。

可以看到,当$("div")的所有任务执行完毕后,就会调用最后的done操作。

Jquery中的Promise也可以代表多种结果,出现不同结果时会分别调用相应的回调。我们以ajax调用为例。1.5之前版本中,代码必须写成这样:

而1.5+版本引入了Promise对象后。可以写成如下形式:

那么这种变化有什么好处呢?为什么非要在触发ajax调用之后再附加回调呢?如果ajax要实现很多效果,比如触发动画、插入html、锁定输入等,那么仅仅由负责发出请求那部分应用代码来处理所有这些效果,显然很蠢。只传递Promise就显得很优雅。

更多详细请参考:

Promise虽然是很优雅,但是也只是解决了回调地狱的问题,真正简化javascript异步编程的还是Generator

Generator

生成器是ES6中的语法。

何为生成器?让我们先看看以下代码

rush:js;"> function* quips(name) { yield "hello " + name + "!"; yield "i hope you are enjoying the blog posts"; if (name.startsWith("X")) { yield "it's cool how your name starts with X," + name; } yield "see you later!"; }

你没有看错,这就是javascript代码。是不是和你曾经认识的javascript不太一样。这个函数就叫做生成函数生成函数看起来和普通的函数是不是有点相像呢?

它们的区别如下:

一般的函数以function开头,而生成函数以function* 开头。

生成函数中有一个特殊关键字就是yield,作用就是暂停函数。配合next方法调用可以达到一步一步的执行函数的目的。

我们看看next方法的使用:

var iter = quips("jorendorff"); [object Generator] > iter.next() { value: "hello jorendorff!",done: false } > iter.next() { value: "i hope you are enjoying the blog posts",done: false } > iter.next() { value: "see you later!",done: false } > iter.next() { value: undefined,done: true }

可以看到,每一次next方法后,生成函数就执行到下一个yield位置处。

下面我们讲解如何通过生成函数来取代回调函数。我们还是以一开始的多次延迟delay为例。

首先,我们需要定义一个生成器:

rush:js;"> function* myDelayedMessages() { /* delay 1000 ms and print the result */ /* delay 1200 ms and print the result */ }

然后我们需要设置delay时间来执行指定操作,我们现在暂定为空函数

rush:js;"> function* myDelayedMessages() { console.log(delay(1000,function(){})); console.log(delay(1200,function(){})); }

然后我们使用yield关键字:

rush:js;"> function* myDelayedMessages() { console.log(yield delay(1000,function(){})); console.log(yield delay(1200,function(){})); }

然后我们就需要指定一个函数run来调用生成函数的next方法,并且将空函数改为参数resume:

rush:js;"> function run(generatorFunction) { var generatorItr = generatorFunction(resume); function resume(callbackValue) { generatorItr.next(callbackValue); } generatorItr.next() }

后执行代码如下:

"Slept for 1000" //...waits 1200ms // > "Slept for 1200"

这样就完美的避免了回调地狱噢!

更多关于JavaScript相关内容可查看本站专题:《》、《》、《》、《》、《》、《》、《》、《》及《用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

相关文章

什么是深拷贝与浅拷贝?深拷贝与浅拷贝是js中处理对象或数据...
前言 今天复习了一些前端算法题,写到一两道比较有意思的题:...
最近在看回JavaScript的面试题,this 指向问题是入坑前端必须...
js如何实现弹出form提交表单?(图文+视频)
js怎么获取复选框选中的值
js如何实现倒计时跳转页面