基于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 相关文章推荐
地址栏上的一段语句,改变页面的风格。(教程)
Apr 02 Javascript
关于javascript中的parseInt使用技巧
Sep 03 Javascript
Mootools 1.2教程 输入过滤第二部分(字符串)
Sep 15 Javascript
jQuery中live方法的重复绑定说明
Oct 21 Javascript
jQuery EasyUI API 中文文档 - TreeGrid 树表格使用介绍
Nov 21 Javascript
JS中类或对象的定义说明
Mar 10 Javascript
innerHTML在IE中报错解决方案
Dec 15 Javascript
JavaScript随机生成颜色的方法
Oct 15 Javascript
详解在vue-cli项目中安装node-sass
Jun 21 Javascript
vue输入框使用模糊搜索功能的实现代码
May 26 Javascript
JavaScript语法约定和程序调试原理解析
Nov 03 Javascript
解决vue watch数据的方法被调用了两次的问题
Nov 07 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
解析在zend Farmework下如何创立一个FORM表单
2013/06/28 PHP
详细解读PHP中接口的应用
2015/08/12 PHP
yii2带搜索功能的下拉框实例详解
2016/05/12 PHP
PHP 与 UTF-8 的最佳实践详细介绍
2017/01/04 PHP
jquery实现的元素的left增加N像素 鼠标移开会慢慢的移动到原来的位置
2010/03/21 Javascript
jquery.simple.tree插件 更简单,兼容性更好的无限树插件
2010/09/03 Javascript
jQuery ul标签下拉菜单演示代码
2010/12/11 Javascript
jQuery 联动日历实现代码
2012/05/31 Javascript
Javascript 实现的数独解题算法网页实例
2013/10/15 Javascript
快速使用Bootstrap搭建传送带
2016/05/06 Javascript
Bootstrap实现带暂停功能的轮播组件(推荐)
2016/11/25 Javascript
详解如何让InstantClick兼容MathJax、百度统计等
2017/09/12 Javascript
详解性能更优越的小程序图片懒加载方式
2018/07/18 Javascript
AngularJS $http post 传递参数数据的方法
2018/10/09 Javascript
js验证密码强度解析
2020/03/18 Javascript
vue项目,代码提交至码云,iconfont的用法说明
2020/07/30 Javascript
Python实例分享:快速查找出被挂马的文件
2014/06/08 Python
解决Python print输出不换行没空格的问题
2018/11/14 Python
解决Python中list里的中文输出到html模板里的问题
2018/12/17 Python
用Python和WordCloud绘制词云的实现方法(内附让字体清晰的秘笈)
2019/01/08 Python
Python图像处理库PIL的ImageFont模块使用介绍
2020/02/26 Python
Python semaphore evevt生产者消费者模型原理解析
2020/03/18 Python
解决keras模型保存h5文件提示无此目录问题
2020/07/01 Python
python中pathlib模块的基本用法与总结
2020/08/17 Python
CSS3+HTML5+JS 实现一个块的收缩与展开动画效果
2020/11/17 HTML / CSS
连卡佛中国官网:Lane Crawford中文站
2018/01/27 全球购物
校园新闻广播稿5篇
2014/10/10 职场文书
2014年卫生保健工作总结
2014/12/08 职场文书
中秋节主题班会
2015/08/14 职场文书
《小摄影师》教学反思
2016/02/18 职场文书
《实心球》教学反思
2016/02/23 职场文书
go语言中json数据的读取和写出操作
2021/04/28 Golang
Python绘制地图神器folium的新人入门指南
2021/05/23 Python
PHP实现rar解压读取扩展包小结
2021/06/03 PHP
Python Matplotlib绘制两个Y轴图像
2022/04/13 Python
解决Mysql中的innoDB幻读问题
2022/04/29 MySQL