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

本文实例讲述了基于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/>');
}

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

说明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程序设计有所帮助。

(0)

相关推荐

  • javascript异步编程代码书写规范Promise学习笔记

    最近工作轻松了点,想起了以前总是看到的一个单词promise,于是耐心下来学习了一下. 一:Promise是什么?为什么会有这个东西? 首先说明,Promise是为了解决javascript异步编程时候代码书写的方式产生的. 随着javascript的发展,异步的场景越来越多.前端有AJAX,setTimeout等,后端Node异步更多.按照传统的做法,那么就是各种回调嵌回调.代码可以把人绕晕. 这个时候,CommonJS社区提出了一个叫做Promise/A+的规范,这个规范定义了如何书写异步代

  • 关于Promise 异步编程的实例讲解

    实例如下所示: //1.解决异步回调问题 //1.1 如何同步异步请求 //如果几个异步操作之间并没有前后顺序之分,但需要等多个异步操作都完成后才能执行后续的任务,无法实现并行节约时间 const fs = require('fs'); let school = {}; fs.readFile('./name.txt','utf8',function (err,data) { school.name = data; }); fs.readFile('./age.txt','utf8',funct

  • Javascript异步编程模型Promise模式详细介绍

    Promise 编程模式也被称为 thenable,可以理解为 延迟后执行.每个 Promise 都拥有一个叫做 then 的唯一接口,当 Promise 失败或成功时,它就会进行回调.它代表了一种可能会长时间运行而且不一定必须完成的操作结果.这种模式不会阻塞和等待长时间的操作完成,而是返回一个代表了承诺的(promised)结果的对象. 当前的许多 JavaScript 库(如 jQuery 和 Dojo.AngularJS)均添加了这种称为 Promise 的抽象.通过这些库,开发人员能够在

  • JavaScript异步编程Promise模式的6个特性

    在我们开始正式介绍之前,我们想看看Javascript Promise的样子: 复制代码 代码如下: var p = new Promise(function(resolve, reject) {  resolve("hello world");}); p.then(function(str) {  alert(str);}); 1. then()返回一个Forked Promise 以下两段代码有什么区别呢? 复制代码 代码如下: // Exhibit Avar p = new Pr

  • 异步JavaScript编程中的Promise使用方法

    异步? 我在很多地方都看到过异步(Asynchronous)这个词,但在我还不是很理解这个概念的时候,却发现自己常常会被当做"已经很清楚"(* ̄? ̄). 如果你也有类似的情况,没关系,搜索一下这个词,就可以得到大致的说明.在这里,我会对JavaScript的异步做一点额外解释. 看一下这段代码: var start = new Date(); setTimeout(function(){ var end = new Date(); console.log("Time elap

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

    本文实例讲述了基于javascript的异步编程.分享给大家供大家参考,具体如下: 异步函数这个术语有点名不副实,调用一个函数后,程序只在该函数返回后才能继续.JavaScript程序员如果称一个函数为异步的,其意思就是这个函数会导致将来再运行另一个函数,后者取自于事件队列.如果后面这个函数是作为参数传递给前者的,则称其为回调函数. callback 回调函数是异步编程最基本的方式. 采用这种方式,我们把同步操作变成了异步操作,主函数不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟

  • Java基于Socket实现网络编程实例详解

    一,网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输. 在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以唯一地确定Internet上的一台主机. 而TCP层则提供面向应用的可靠(tcp)的或非可靠(UDP)的数据传输机制,这是网络编程的主要对象,一般不需要关心IP层是如何处理数据的. 目前较为流行的网络编程模型是客户机/服务器(C/S)结构.即通信双方一方作为服务器等待客户提出请求并予以响应.客户则

  • nodejs中的异步编程知识点详解

    简介 因为javascript默认情况下是单线程的,这意味着代码不能创建新的线程来并行执行.但是对于最开始在浏览器中运行的javascript来说,单线程的同步执行环境显然无法满足页面点击,鼠标移动这些响应用户的功能.于是浏览器实现了一组API,可以让javascript以回调的方式来异步响应页面的请求事件. 更进一步,nodejs引入了非阻塞的 I/O ,从而将异步的概念扩展到了文件访问.网络调用等. 今天,我们将会深入的探讨一下各种异步编程的优缺点和发展趋势. 同步异步和阻塞非阻塞 在讨论n

  • C++中Socket网络编程实例详解

    C++中Socket网络编程实例详解 现在几乎所有C/C++的后台程序都需要进行网络通讯,其实现方法无非有两种:使用系统底层socket或者使用已有的封装好的网络库.本文对两种方式进行总结,并介绍一个轻量级的网络通讯库ZeroMQ.  1.基本的Scoket编程 关于基本的scoket编程网络上已有很多资料,作者在这里引用一篇文章中的内容进行简要说明. 基于socket编程,基本上就是以下6个步骤: 1.socket()函数 2.bind()函数 3.listen().connect()函数 4

  • Java CountDownLatch完成异步回调实例详解

    Java CountDownLatch完成异步回调实例详解 实例代码: public class AsyncDemo { private static void doSomeTask() { System.out.println("Hello World"); } private static void onCompletion() { System.out.println("All tasks finished"); } public static void ma

  • C++ 中 socket编程实例详解

    C++ 中 socket编程实例详解 sockets(套接字)编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW):基于TCP的socket编程是采用的流式套接字.在这个程序中,将两个工程添加到一个工作区.要链接一个ws2_32.lib的库文件. 服务器端编程的步骤: 1:加载套接字库,创建套接字(WSAStartup()/socket()): 2:绑定套接字到一个IP地址和一个端口上(bind()): 3:将套接字设置为监听模式

  • linux下的C\C++多进程多线程编程实例详解

    linux下的C\C++多进程多线程编程实例详解 1.多进程编程 #include <stdlib.h> #include <sys/types.h> #include <unistd.h> int main() { pid_t child_pid; /* 创建一个子进程 */ child_pid = fork(); if(child_pid == 0) { printf("child pid\n"); exit(0); } else { print

  • javascript 中的继承实例详解

    javascript 中的继承实例详解 阅读目录 原型链继承 借用构造函数 组合继承 寄生组合式继承 后记 继承有两种方式:接口继承和实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法. 由于函数没有签名,在ECMAScript中无法实现接口继承.ECMAScript只支持实现继承,而且实现继承主要依靠原型链来实现. 下面介绍几种js的继承: 原型链继承 原型链继承实现的本质是重写原型对象,代之以一个新类型的实例.代码如下: function SuperType() { this.pr

  • php基于redis的分布式锁实例详解

    在使用分布式锁进行互斥资源访问时候,我们很多方案是采用redis的实现. 固然,redis的单节点锁在极端情况也是有问题的,假设你的业务允许偶尔的失效,使用单节点的redis锁方案就足够了,简单而且效率高. redis锁失效的情况: 客户端1从master节点获取了锁 master宕机了,存储锁的key还没来得及同步到slave节点上 slave升级为master 客户端2从新的master上获取到同一个资源的锁 于是,客户端1和客户端2同事持有了同一个资源的锁,锁的安全性被打破. 如果我们不考

  • 基于JavaScript实现游戏购物车效果详解

    目录 项目展示 登陆界面 注册界面 主界面 项目展示 登陆界面 登陆界面html和js <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <tit

随机推荐