前言

异步最早的解决方案是回调函数,如ajax,事件的回调,setInterval/setTimeout中的回调。但是回调函数有回调地狱的问题;

为了解决回调地狱的问题,社区提出了Promise解决方案,ES6将其写进了语言标准。Promise一定程度上解决了回调地狱的问题,但是Promise也存在一些问题,如错误不能被try catch,而且使用Promise的链式调用,其实并没有从根本上解决回调地狱的问题,只是换了一种写法。

ES6中引入 Generator 函数,Generator是一种异步编程解决方案,Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权,Generator 函数可以看出是异步任务的容器,需要暂停的地方,都用yield语句注明。但是 Generator 使用起来较为复杂。

ES7又提出了新的异步解决方案:async/await,async是 Generator 函数的语法糖,async/await 使得异步代码看起来像同步代码,异步编程发展的目标就是让异步逻辑的代码看起来像同步一样。

同步和异步

  关于同步和异步的概念,这里给出阮一峰老师的文章参考。其中也不乏一些异步的解决办法例子,如《js异步编程的4种方法》,《异步操作概述》。

异步解决方案

  回调函数

 // node读取文件
fs.readFile('url', 'utf-8', function(err, data) {
// code
});

  使用的场景有:ajax请求,事件回调函数,Node API,定时器等。

   优点: 简单。 缺点: 异步回调嵌套会导致代码难以维护,并且不方便统一处理错误,不能 try,catch 和存在回调地狱问题。

// 回调地狱问题,以读取A文本内容,再根据A文本内容读取B再根据B的内容读取C..为例
fs.readFile('A', 'utf-8', function(err, data) {
fs.readFile('B', 'utf-8', function(err, data) {
fs.readFile('C', 'utf-8', function(err, data) {
fs.readFile('D', 'utf-8', function(err, data) {
// code
});
});
});
});

Promise

  Promise 一定程度上解决了回调地狱的问题,Promise 最早由社区提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。《ECMAScript 6 入门》

  我们来观察一下Promise是如何解决回调地狱问题的。

function read(url) {
return new Promise((resolve, reject) => {
fs.readFile(url, 'utf-8', function(err, data) {
if (err) reject(err);
resolve(data);
});
});
}
read('A').then(data => {
return read('B');
}).then(data => {
return read('C');
}).then(data => {
return read('D');
}).catch(err => {
console.log(err);
});

    promise的优点: 1.一旦状态改变,就不会再变,任何时候都可以得到这个结果。2.可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数

promise的缺点:1.无法取消 Promise。 2.当处于pending状态时,无法得知目前进展到哪一个阶段。 3.错误不能被 trycatch

这里举一个在promise之前,读取A,B,C三个文件内容,都读取成功后,再输出最终的结果例子。依赖于发布订阅模式

let pubsup = {
arry: [],
emit: function() {
this.arry.forEach(fn => fn());
},
on: function(fn) {
this.arry.push(fn);
}
};
let data = [];
pubsub.on(() => {
if (data.length == 3) {
console.log(data);
}
});
fs.readFile('A', 'utf-8', function(err, data) {
data.push(data);
pubsup.emit();
});
fs.readFile('B', 'utf-8', function(err, data) {
data.push(data);
pubsup.emit();
});
fs.readFile('C', 'utf-8', function(err, data) {
data.push(data);
pubsup.emit();
});

顺便提一下朴灵的深入浅出nodejs里面介绍不同场景下异步获取数据进行操作的实践,非常值得看。

Promise给我们提供了 Promise.all 的方法,对于这个需求,我们可以使用 Promise.all 来实现。

/**
* 将 fs.readFile 包装成promise接口
*/
function read(url) {
return new Promise((resolve, reject) => {
fs.readFile(url, 'utf-8', function(err, data) {
if (err) reject(err);
resolve(data);
});
});
}
/**
* Promise.all 可以实现多个异步并行执行,同一时刻获取最终结果的问题
*/
Promise.all([
read(A),
read(B),
read(C)
]).then(data => {
console.log(data);
}).catch(err => console.log(err));

Generator

Generator 函数是 ES6 提供的一种异步编程解决方案,整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用 yield 语句注明。

Generator 函数一般配合 yield 或 Promise 使用。Generator函数返回的是迭代器。对生成器和迭代器不了解的同学参考下阮一峰的ES6入门

function* generator() {
let a = yield 111;
console.log(a);
let b = yield 222;
console.log(b);
let c = yield 333;
console.log(c);
let d = yield 444;
console.log(d);
}
let t = generator();
// next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值
t.next(1); // 第一次调用next函数时,传递的参数无效
t.next(2); // 输出2
t.next(3); //
t.next(4); //
t.next(5); //

仍然以上文的 readFile (先读取A文本内容,再根据A文本内容读取B再根据B的内容读取C)为例,使用 Generator + co库来实现:

const fs = require('fs');
const co = require('co');
const bluebird = require('bluebird');
const readFile = bluebird.promiseify(fs.readFile);
function* read() {
yield readFile('A', 'utf-8');
yield readFile('B', 'utf-8');
yield readFile('C', 'utf-8');
}
co(read()).then(data => {
// code
}).catch(err => {
// code
});

await/async

ES7中引入了 async/await 概念。async 其实是一个语法糖,它的实现就是将 Generator函数和自动执行器(co),包装在一个函数中。

async/await 的优点是代码清晰,不用像 Promise 写很多 then 链,就可以处理回调地狱的问题。并且错误可以被try catch。

仍然以上文的readFile (先读取A文本内容,再根据A文本内容读取B再根据B的内容读取C) 为例,使用 async/await 来实现:

const fs = require('fs');
const co = require('co');
const bluebird = require('bluebird');
const readFile = bluebird.promiseify(fs.readFile);
async function read() {
await readFile('A', 'utf-8');
await readFile('B', 'utf-8');
await readFile('C', 'utf-8');
}
co(read()).then(data => {
// code
}).catch(err => {
// code
});

  我们用async/await来实现同样的效果。

function read(url) {
return new Promise((resolve, reject) => {
fs.readFile(url, 'utf-8', function(err, data) {
if (err) reject(err);
resolve(data);
})
});
}
async function readAsync() {
const data = await Promise.all([
read(A),
read(B),
read(C)
]);
return data;
}
readSync().then(data => {
console.log(data);
})

所以JS的异步发展史,可以认为是从 callback -> promise -> generator -> async/await。async/await 使得异步代码看起来像同步代码,异步编程发展的目标就是让异步逻辑的代码看起来像同步一样

JS异步解决方案的更多相关文章

  1. JS异步解决方案之概念理解-----------阻塞和非阻塞,同步和异步,并发和并行,单线程和多线程

    首先记住一句话,JS是单线程的. 单线程意味着什么?单线程意味着 它不能依靠自己实现异步. JS实现的异步,往往都是靠 浏览器.Node 的机制(事件驱动.回调)实现的. 下面让我这个单身狗 以谈恋爱 ...

  2. node.js异步编程解决方案之Promise用法

    node.js异步编程解决方案之Promise var dbBase = require('../db/db_base'); var school_info_db = require('../db/s ...

  3. js async await 终极异步解决方案

    既然有了promise 为什么还要有async await ? 当然是promise 也不是完美的异步解决方案,而 async await 的写法看起来更加简单且容易理解. 回顾 Promise Pr ...

  4. 关于使用Iscroll.js异步加载数据后不能滑动到最底端的问题解决方案

    关于使用Iscroll.js异步加载数据后不能滑动到最底端,拉到最下边又弹回去的问题困扰了我老半天,相信很多朋友都遇到了.我刚好不小心解决了,和大家分享一下.由于各种忙,下边就直接上代码吧. (前提是 ...

  5. [转] js async await 终极异步解决方案

    阅读目录 回顾 Promise async await 字面理解 async.await 如何执行 await 操作符 总结 既然有了promise 为什么还要有async await ? 当然是pr ...

  6. js异步编程

    前言 以一个煮饭的例子开始,例如有三件事,A是买菜.B是买肉.C是洗米,最终的结果是为了煮一餐饭.为了最后一餐饭,可以三件事一起做,也可以轮流做,也可能C需要最后做(等A.B做完),这三件事是相关的, ...

  7. 关于JS异步加载方案

    javascript延迟加载的解决方案: 1.使用defer标签 <span style="font-size: small;"><script type=&qu ...

  8. js 异步流程控制之 avQ(avril.queue)

    废话前言 写了多年的js,遇到过最蛋疼的事情莫过于callback hell, 相信大家也感同身受. 业界许多大大也为此提出了很多不错的解决方案,我所了解的主要有: 朴灵 event proxy, 简 ...

  9. 前端分享----JS异步编程+ES6箭头函数

    前端分享----JS异步编程+ES6箭头函数 ##概述Javascript语言的执行环境是"单线程"(single thread).所谓"单线程",就是指一次只 ...

随机推荐

  1. JAVA代码规范 标签: java文档工作 2016-06-12 21:50 277人阅读 评论(5) 收藏

    开始做java的ITOO了,近期的工作内容就是按照代码规范来改自己负责的代码,之前做机房收费系统的时候,也是经常验收的,甚至于我们上次验收的时候,老师也去了.对于我们的代码规范,老师其实是很重视的,他 ...

  2. 【UML】之简单概括 标签: uml图形 2014-11-09 11:24 1130人阅读 评论(24) 收藏

    29号开始看UML的视频,由于之前看视频总是一拖拖上半个月,所以这次打算速战速决,而且UML视频的讲解和内容并不算多,也比较容易懂,到后期更是花了很多时间来举例子巩固各种图的画法,所以这次花了11天初 ...

  3. HDU_2035:人见人爱A^B

    Problem Description 求A^B的最后三位数表示的整数. 说明:A^B的含义是“A的B次方”   Input 输入数据包含多个测试实例,每个实例占一行,由两个正整数A和B组成(1< ...

  4. HDU_1061:Rightmost Digit

    Problem Description Given a positive integer N, you should output the most right digit of N^N.   Inp ...

  5. 【NS2】trace 文件格式(转载)

    本文档是对 http://nsnam.isi.edu/nsnam/index.php/NS-2_Trace_Formats > 的翻译. 译注:本文描述的无线格Trace格式已经有些陈旧,现在一 ...

  6. Vue组件跨层级通信

    正常组件间通信 父->子组件 是通过属性传递 子->父组件 是通过this.$emit()传递 this.$emit()返回的是this,如果需要一些值 可使用callback方式传递 p ...

  7. 威胁快报|挖矿团伙8220进化,rootkit挖矿趋势兴起

    近日,阿里云安全团队发现8220挖矿团伙为了更持久的驻留主机以获得最大收益,开始使用rootkit技术来进行自我隐藏.这类隐藏技术的使用在watchdogs等挖矿蠕虫使用后开始出现逐渐扩散和进化的趋势 ...

  8. [wikipedia] List of free and open-source software packages

    List of free and open-source software packages From Wikipedia, the free encyclopedia     This articl ...

  9. Datamation Index

    Datamation Index     Understand how to handle big data and improve organizational agility to support ...

  10. js循环遍历数组(对象)

    1,for循环 对于循环应该是最常用的一种遍历方式了,通常用来遍历数组结构. let arr = [a,b,d];for (let i=0; i<arr.length; i++){ consol ...