基于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 相关文章推荐
jquery 学习笔记一
Apr 07 Javascript
jquery属性选择器not has怎么写 行悬停高亮显示
Nov 13 Javascript
清除div下面的所有标签的方法
Feb 17 Javascript
javascript判断并获取注册表中可信任站点的方法
Jun 01 Javascript
JS实现获取当前URL和来源URL的方法
Aug 24 Javascript
Vue实现动态响应数据变化
Apr 28 Javascript
使用JS实现图片轮播的实例(前后首尾相接)
Sep 21 Javascript
详解vue 组件之间使用eventbus传值
Oct 25 Javascript
Angular2中监听数据更新的方法
Aug 31 Javascript
从0到1搭建Element的后台框架的方法步骤
Apr 10 Javascript
layui实现form表单同时提交数据和文件的代码
Oct 25 Javascript
jQuery模仿ToDoList实现简单的待办事项列表
Dec 30 jQuery
浅谈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
解析百度搜索结果link?url=参数分析 (全)
2012/10/09 PHP
Laravel 5框架学习之向视图传送数据
2015/04/08 PHP
tp5.1 框架数据库-数据集操作实例分析
2020/05/26 PHP
js查找父节点的简单方法
2008/06/28 Javascript
FLASH 广告之外的链接
2008/12/16 Javascript
JS实现的通用表单验证插件完整实例
2015/08/20 Javascript
jQuery实现两款有动画功能的导航菜单代码
2015/09/16 Javascript
点评js异步加载的4种方式
2015/12/22 Javascript
JavaScript中的this引用(推荐)
2016/08/05 Javascript
jquery注册文本框获取焦点清空,失去焦点赋值的简单实例
2016/09/08 Javascript
javascript常用经典算法详解
2017/01/11 Javascript
使用vue-resource进行数据交互的实例
2017/09/02 Javascript
关于JavaScript语句后面的分号问题
2017/12/07 Javascript
基于jQuery的时间戳与日期间的转化
2019/06/21 jQuery
layui实现数据表格点击搜索功能
2020/03/26 Javascript
vue按需加载实例详解
2019/09/06 Javascript
[15:58]DOTA2国际邀请赛采访专栏:Tongfu.Sansheng&KingJ,DK.rOtk
2013/08/08 DOTA
python中lambda与def用法对比实例分析
2015/04/30 Python
使用Python脚本和ADB命令实现卸载App
2017/02/10 Python
python线程池threadpool使用篇
2018/04/27 Python
Linux下Pycharm、Anaconda环境配置及使用踩坑
2018/12/19 Python
OpenCV-Python 摄像头实时检测人脸代码实例
2019/04/30 Python
将tensorflow.Variable中的某些元素取出组成一个新的矩阵示例
2020/01/04 Python
Moda Italia荷兰:意大利男士服装
2019/08/31 全球购物
会计专业毕业自荐书范文
2014/02/08 职场文书
商务日语专业毕业生自荐信
2014/03/27 职场文书
校园绿化美化方案
2014/06/08 职场文书
客户答谢会活动方案
2014/08/31 职场文书
民主评议党员登记表自我评价
2014/10/20 职场文书
销售经理工作检讨书
2015/02/19 职场文书
2015年高三教学工作总结
2015/07/21 职场文书
医院保洁员管理制度
2015/08/05 职场文书
《称赞》教学反思
2016/02/17 职场文书
2016年小学教师师德承诺书
2016/03/25 职场文书
Python实战之实现康威生命游戏
2021/04/26 Python
Python 图片添加美颜效果
2022/04/28 Python