基于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 相关文章推荐
一个JS小玩意 几个属性相加不能超过一个特定值.
Sep 29 Javascript
简单实用的全选反选按钮例子
Oct 18 Javascript
javascript修改表格背景色实例代码分享
Dec 10 Javascript
将字符串中由空格隔开的每个单词首字母大写
Apr 06 Javascript
javascipt:filter过滤介绍及使用
Sep 10 Javascript
vue2.0开发实践总结之疑难篇
Dec 07 Javascript
基于javascript实现数字英文验证码
Jan 25 Javascript
基于EasyUI的基础之上实现树形功能菜单
Jun 28 Javascript
JavaScript高级函数应用之分时函数实例分析
Aug 03 Javascript
在layui中使用form表单监听ajax异步验证注册的实例
Sep 03 Javascript
vue同个按钮控制展开和折叠同个事件操作
Jul 29 Javascript
js简单粗暴的发布订阅示例代码
Jan 23 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
星际原理概述
2020/03/04 星际争霸
PHP中实现生成静态文件的方法缓解服务器压力
2014/01/07 PHP
JS判断是否为数字,是否为整数,是否为浮点数的代码
2010/04/24 Javascript
js判断undefined类型示例代码
2014/02/10 Javascript
Javascript 浮点运算精度问题分析与解决
2014/03/26 Javascript
js检测判断日期大于多少天的方法
2015/05/04 Javascript
JavaScript实现给按钮加上双重动作的方法
2015/08/14 Javascript
JS实现仿苹果底部任务栏菜单效果代码
2015/08/28 Javascript
js实现纯前端的图片预览
2016/04/27 Javascript
基于d3.js实现实时刷新的折线图
2016/08/03 Javascript
浅谈react.js中实现tab吸顶效果的问题
2017/09/06 Javascript
为jquery的ajax请求添加超时timeout时间的操作方法
2018/09/04 jQuery
微信小程序bindinput与bindsubmit的区别实例分析
2019/04/17 Javascript
postman自定义函数实现 时间函数的思路详解
2019/04/17 Javascript
jQuery+ajax实现批量删除功能完整示例
2019/06/06 jQuery
微信小程序 腾讯地图显示偏差问题解决
2019/07/27 Javascript
再也不怕 JavaScript 报错了,怎么看怎么处理都在这儿
2020/12/09 Javascript
Python的装饰器模式与面向切面编程详解
2015/06/21 Python
django允许外部访问的实例讲解
2018/05/14 Python
Python读写zip压缩文件的方法
2018/08/29 Python
Python3 单行多行万能正则匹配方法
2019/01/07 Python
python实现网页自动签到功能
2019/01/21 Python
Python函数的返回值、匿名函数lambda、filter函数、map函数、reduce函数用法实例分析
2019/12/26 Python
解决python运行启动报错问题
2020/06/01 Python
css3加js做一个简单的3D行星运转效果实例代码
2017/01/18 HTML / CSS
HTML5 Canvas实现放大镜效果示例
2020/03/25 HTML / CSS
夏威夷航空官网:Hawaiian Airlines
2016/09/11 全球购物
Perfume’s Club澳大利亚官网:西班牙领先的在线美容店
2021/02/01 全球购物
试解释COMMIT操作和ROLLBACK操作的语义
2014/07/25 面试题
自我鉴定书面格式
2014/01/13 职场文书
营销总经理岗位职责
2014/02/02 职场文书
教师工作总结范文2014
2014/11/10 职场文书
大学学生个人总结
2015/02/15 职场文书
留学推荐信怎么写
2015/03/26 职场文书
创业计划书之家教托管
2019/09/25 职场文书
Django框架中模型的用法
2022/06/10 Python