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


Posted in Javascript onApril 10, 2017

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

异步函数这个术语有点名不副实,调用一个函数后,程序只在该函数返回后才能继续。JavaScript程序员如果称一个函数为异步的,其意思就是这个函数会导致将来再运行另一个函数,后者取自于事件队列。如果后面这个函数是作为参数传递给前者的,则称其为回调函数。

callback

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

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

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

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

function delay(time, callback) {
 setTimeout(function () {
 callback("Slept for "+time);
 }, time);
}

那么如果我要delay两次呢?

delay(1000, function(msg) {
 console.log(msg);
 delay(1200, function (msg) {
 console.log(msg);
 }
})
//...waits 1000ms
// > "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。这样一来,所有的潜在风险就都在A的可控范围之内了。

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

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

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

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

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

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

$("button").bind( "click", function() {
 $("p").append( "Started...");
 $("div").each(function( i ) {
 $( this ).fadeIn().fadeOut( 1000 * (i+1) );
 });
 $( "div" ).promise().done(function() {
 $( "p" ).append( " Finished! " );
 });
});

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

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

$.get('/getdata',{
 success:onSuccess,
 failure:onFailure,
 always:onAlways
});

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

var promise = $.get('/getdata');
promise.done(onSuccess);
promise.fail(onFailure);
promise.always(onAlways);

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

更多详细请参考:jquery

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

Generator

生成器是ES6中的语法。

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

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为例。

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

function* myDelayedMessages() {
 /* delay 1000 ms and print the result */
 /* delay 1200 ms and print the result */
}

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

function* myDelayedMessages() {
 console.log(delay(1000, function(){}));
 console.log(delay(1200, function(){}));
}

然后我们使用yield关键字:

function* myDelayedMessages() {
 console.log(yield delay(1000, function(){}));
 console.log(yield delay(1200, function(){}));
}

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

function run(generatorFunction) {
 var generatorItr = generatorFunction(resume);
 function resume(callbackValue) {
 generatorItr.next(callbackValue);
 }
 generatorItr.next()
}

最后执行代码如下:

run(function* myDelayedMessages(resume) {
 console.log(yield delay(1000, resume));
 console.log(yield delay(1200, resume));
})
//...waits 1000ms
// > "Slept for 1000"
//...waits 1200ms
// > "Slept for 1200"

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

更多关于JavaScript相关内容可查看本站专题:《javascript面向对象入门教程》、《JavaScript中json操作技巧总结》、《JavaScript切换特效与技巧总结》、《JavaScript查找算法技巧总结》、《JavaScript动画特效与技巧汇总》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》

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

Javascript 相关文章推荐
javascript 类定义的4种方法
Sep 12 Javascript
Cookie 小记
Apr 01 Javascript
JS对外部文件的加载及对IFRMAME的加载的实现,当加载完成后,指定指向方法(方法回调)
Jul 04 Javascript
JQuery1.6 使用方法三
Nov 23 Javascript
3款实用的在线JS代码工具(国外)
Mar 15 Javascript
PHP使用方法重载实现动态创建属性的get和set方法
Nov 17 Javascript
基于JS实现密码框(password)中显示文字提示功能代码
May 27 Javascript
jquery延迟对象解析
Oct 26 Javascript
Vue实现数字输入框中分割手机号码的示例
Oct 10 Javascript
JS手机端touch事件计算滑动距离的方法示例
Oct 26 Javascript
js限制input只能输入有效的数字(第一个不能是小数点)
Sep 28 Javascript
微信小程序class封装http代码实例
Aug 24 Javascript
浅谈js-FCC算法Friendly Date Ranges(详解)
Apr 10 #Javascript
javascript实现table单元格点击展开隐藏效果(实例代码)
Apr 10 #Javascript
js实现适配不同的屏幕大小
Apr 10 #Javascript
JS闭包可被利用的常见场景小结
Apr 09 #Javascript
Django1.7+JQuery+Ajax验证用户注册集成小例子
Apr 08 #jQuery
作为老司机使用 React 总结的 11 个经验教训
Apr 08 #Javascript
详解angular element()方法使用
Apr 08 #Javascript
You might like
PHP大小写问题:函数名和类名不区分,变量名区分
2013/06/17 PHP
thinkphp实现发送邮件密码找回功能实例
2014/12/01 PHP
laravel5.4利用163邮箱发送邮件的步骤详解
2017/09/22 PHP
PHP连接MySQL数据库三种实现方法
2020/12/10 PHP
Javascript表达式中连续的 &amp;&amp; 和 || 之赋值区别
2010/10/17 Javascript
新鲜出炉的js tips提示效果
2011/04/03 Javascript
深入理解JavaScript系列(9) 根本没有“JSON对象”这回事!
2012/01/15 Javascript
JavaScript中toString()方法的使用详解
2015/06/05 Javascript
javascript解决IE6下hover问题的方法
2015/07/28 Javascript
基于jquery实现select选择框内容左右移动添加删除代码分享
2015/08/25 Javascript
thinkphp实现无限分类(使用递归)
2015/12/19 Javascript
JS基于MSClass和setInterval实现ajax定时采集信息并滚动显示的方法
2016/04/18 Javascript
Vue如何实现组件的源码解析
2017/06/08 Javascript
使用JQuery实现图片轮播效果的实例(推荐)
2017/10/24 jQuery
命令行批量截图Node脚本示例代码
2019/01/25 Javascript
Vue页面切换和a链接的本质区别详解
2019/11/12 Javascript
微信小程序地图实现展示线路
2020/07/29 Javascript
微信小程序:报错(in promise) MiniProgramError
2020/10/30 Javascript
六个窍门助你提高Python运行效率
2015/06/09 Python
Python 错误和异常代码详解
2018/01/29 Python
Tensorflow的可视化工具Tensorboard的初步使用详解
2018/02/11 Python
pycharm使用matplotlib.pyplot不显示图形的解决方法
2018/10/28 Python
python 删除字符串中连续多个空格并保留一个的方法
2018/12/22 Python
Python根据当前日期取去年同星期日期
2019/04/14 Python
详解Python 中sys.stdin.readline()的用法
2019/09/12 Python
python如何进入交互模式
2020/07/06 Python
美国最大的万圣节服装网站:HalloweenCostumes.com
2017/10/12 全球购物
澳大利亚第一旅行车和房车配件店:Caravan RV Camping
2020/12/26 全球购物
Java如何调用外部Exe程序
2015/07/04 面试题
学校安全检查制度
2014/01/27 职场文书
歌唱比赛主持词
2014/03/18 职场文书
应聘会计求职信
2014/06/11 职场文书
2014年国庆节庆祝建国65周年比赛演讲稿
2014/09/21 职场文书
学校光盘行动倡议书
2015/04/28 职场文书
MySQL令人大跌眼镜的隐式转换
2021/08/23 MySQL
Sql Server 行数据的某列值想作为字段列显示的方法
2022/04/20 SQL Server