需求:

A、依次读取 A|B|C 三个文件,如果有失败,则立即终止。

B、同时读取 A|B|C 三个文件,如果有失败,则立即终止。

一、callback


需求A


let read = function (code) {
if (code) {
return true;
} else {
return false;
}
} let readFileA = function (callback) {
if (read(1)) {
return callback(null, "111");
} else {
return callback("a fail");
}
}
let readFileB = function (callback) {
if (read(1)) {
return callback(null, "222");
} else {
return callback("b fail");
}
}
let readFileC = function (callback) {
if (read(1)) {
return callback(null, "333");
} else {
return callback("c fail");
}
} readFileA(function (err, data) {
if (err) {
console.log("open file " + err);
return;
}
console.log("读取 a.txt 成功!内容:" + data);
readFileB(function (err, data) {
if (err) {
console.log("open file " + err);
return;
}
console.log("读取 b.txt 成功!内容:" + data);
readFileC(function (err, data) {
if (err) {
console.log("open file " + err);
return;
}
console.log("读取 c.txt 成功!内容:" + data);
});
});
});

return:

读取 a.txt 成功!内容:111
读取 b.txt 成功!内容:222
读取 c.txt 成功!内容:333

需求B:太恶心了,不写了,总之很繁琐.

二、async.js


async.js 库的详细介绍可以见:[待写]

需求A

async.series

var async = require("async");

let read = function (code) {
if (code) {
return true;
} else {
return false;
}
} let readFileA = function (callback) {
if (read(1)) {
return callback(null, "111");
} else {
return callback("a fail");
}
} let readFileB = function (callback) {
if (read(0)) {
return callback(null, "222");
} else {
return callback("b fail");
}
}
let readFileC = function (callback) {
if (read(1)) {
return callback(null, "333");
} else {
return callback("c fail");
}
} async.series([readFileA, readFileB, readFileC],
function (err, datas) {
if (err) {
console.log("open file " + err);
}
console.log(datas);
return;
});

当第二个 readFileB() 读取失败的话:

return:

open file b fail
[ '111', undefined ]

需求B

async.parallel

var async = require("async");

let read = function (code) {
if (code) {
return true;
} else {
return false;
}
} let readFileA = function (callback) {
if (read(1)) {
return callback(null, "111");
} else {
return callback("a fail");
}
} let readFileB = function (callback) {
setTimeout(() => {
if (read(0)) {
return callback(null, "222");
} else {
return callback("b fail");
}
}, 1000);
} let readFileC = function (callback) {
if (read(1)) {
return callback(null, "333");
} else {
return callback("c fail");
}
} async.parallel([readFileA, readFileB, readFileC],
function (err, datas) {
if (err) {
console.log("open file " + err);
}
console.log(datas);
return;
});

当第二个 readFileB() 读取失败 (注意我给它加了 setTimeout,为了体现跟上面串行结果的不一样) 的话:

return:

open file b fail
[ '111', undefined, '333' ]

总结:async.js 跟 callback 比的好处:

1、代码量少了,解决了回调地狱金字塔的缺陷

2、async 的第二个参数回调函数里,可以统一处理错误(建议用不同的 Error 类作区分)

3、成功返回的结果 datas 可以汇总到一个数组中方便处理

三、promise


[拓展]

promise 知识

new Promise()


//  promise 在 new 的时候已经开始运行
new Promise(() => console.log("I have already started!"));

return:

I have already started!

promise.then(successCallback, failureCallback);


new Promise((resolve, reject) => resolve()).then(function (data) {
console.log("success");
}, function (data) {
console.log("fail");
})

return:

success

promise.catch(failureCallback)


// promise.catch(failureCallback) 是 promise.then(null, failureCallback) 的缩略形式
new Promise((resolve, reject) => reject()).catch( function (data) {
console.log("fail");
})

return:

fail

链式调用


// 链式调用的原理:then 函数会返回一个新的 promise
new Promise((resolve, reject) => reject()).then(function (data) {
console.log("success_1");
}, function (err) {
console.log("fail_1");
}).then(function (data) {
console.log("success_2");
}, function (err) {
console.log("fail_2");
});

return:

fail_1
success_2

提问

问1:then 函数会返回一个新的 promise,但是 then 的 successCallback 和 failureCallback 这两个回调函数里都没法调用 resolve() 和 reject(),那这个新的 promise 如何指定最终状态呢?

then 的 successCallback 和 failureCallback 里 等同于
不返回 resolve(undefined)
return 1 resolve(1)
return Promise.resolve() resolve()
return Promise.reject() reject()
throw Error() reject()
return new Promise() 以此类推

而普通的 promise 对象,如果不显示调用 resolve/reject ,则没有任何反应,例如:

new Promise((resolve, reject) => {return 1;}).then(function (data) {
console.log("success");
}, function (err) {
console.log("fail");
});

return:

没有任何输出

问2:then 函数如果 successCallbackfailureCallback 都为 null,会发生什么?

什么都不会发生,.then(null, null) 只要一方为 null,等于交给下一个 then 去接管这个回调

new Promise((resolve, reject) => reject())
.then(null, null)
.then(null, null)
.then(null, null)
.then(null, null)
.then(function (data) {
console.log("success_2");
}, function (err) {
console.log("fail_2");
});

所以按照上面 2 个提问揭示的规律,我们可以写成下面优雅的代码

// 链式调用的原理:then 函数会返回一个新的 promise
new Promise((resolve, reject) => resolve()).then((data) => {
console.log("success_1");
}).then((data) => {
console.log("success_2");
throw Error("error");
}).then((data) => {
console.log("success_3");
}).catch((err) => {
console.log(err);
});

return:

success_1
success_2
Error: error ……

注:.catch() 后还可以继续接 .then().catch()

这就达到了如下别人家同步代码的清晰的表达:

try {
let result = syncDoSomething();
let newResult = syncDoSomethingElse(result);
let finalResult = syncDoThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch(error) {
console.log(error);
}

所以,需求A:


let read = function (code) {
if (code) {
return true;
} else {
return false;
}
} let readFileA = function () {
return new Promise(function (resolve, reject) {
if (read(1)) {
resolve("111");
} else {
reject("a fail");
}
});
}
let readFileB = function () {
return new Promise(function (resolve, reject) {
if (read(1)) {
resolve("222");
} else {
reject("b fail");
}
});
}
let readFileC = function () {
return new Promise(function (resolve, reject) {
if (read(1)) {
resolve("333");
} else {
reject("c fail");
}
});
} //[串行] 场景:依次预加载多个资源,如果中途有失败,则进入 .catch()
readFileA().then(function (data) {
console.log("读取 a.txt 成功!内容:" + data);
return readFileB();
}).then(function (data) {
console.log("读取 b.txt 成功!内容:" + data);
return readFileC();
}).then(function (data) {
console.log("读取 c.txt 成功!内容:" + data);
return "读取结束";
}).then(function (data) {
console.log(data);
return;
}).catch(function (err) {
console.log("open file " + err);
})

promise vs 事件监听

a. 事件监听更多的是针对同一对象上发生多次的事情(如 keyup、touchstart 等)

promise 更多的表现这个对象最终走向什么状态,且不可改变。

但有个神奇的特型是一致的,事件监听promise 都可以对同一事件的反应绑定多次的回调函数,如下面例子所示:

let promise = new Promise((resolve, reject) => {
console.log("I have already started!");
resolve();
}) setTimeout(() => {
promise.then(function (data) {
console.log("success_1");
throw new Error();
}, function (err) {
console.log("fail_1");
});
}, 2000); setTimeout(() => {
promise.then(function (data) {
console.log("success_2");
}, function (err) {
console.log("fail_2");
});
}, 4000);

return:

I have already started!
//又等待了2秒
success_1
(node:13150) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error
(node:13150) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
//又等待了2秒
success_2

b. 跟 事件监听不一样,如果 promise 已成功或失败了,过段时间再添加了回调函数,则还是可以成功调用回调。这个上面的例子也可以体现。

c. 事件监听更多的关注某些功能的准确时间,promise 更多地是关注对结果作出的反应。


promise 扩展 API

Promise.resolve()Promise.reject()


手动创建一个已经 resolve 或者 reject 的 promise 的快捷方法。

promise.all:可以实现需求B:


//promise.all [并行] 场景:预加载多个资源,都完成后才能进入页面
Promise.all([readFileA(), readFileB(), readFileC()]).then(function (datas) {
console.log(datas); //所有promise都resolve,返回array
return;
}).catch(function (err) {
console.log("open file " + err); //只要有一个promise是reject,返回这个reject的value
})

promise.race


//promise.race [并行] 场景:taskA:fetch图片,taskB:settimeout抛错,让两个task赛跑实现请求超时报错功能
Promise.race([taskA(), taskB()]).then(function (data) { //进到resolve还是reject回调只取决于第一个确定状态的Promise
console.log(data);
return;
}).catch(function (err) {
console.log("读取图片超时");
})

总结:promise 跟 callback 比的好处:

1、代码量少了,解决了回调地狱金字塔的缺陷

2、.catch 可以统一处理错误(建议用不同的 Error 类作区分)

四、async / await

需求A:

let read = function (code) {
if (code) {
return true;
} else {
return false;
}
} let readFileA = function () {
return new Promise(function (resolve, reject) {
if (read(1)) {
resolve("111");
} else {
reject("a fail");
}
});
}
let readFileB = function () {
return new Promise(function (resolve, reject) {
if (read(1)) {
resolve("222");
} else {
reject("b fail");
}
});
}
let readFileC = function () {
return new Promise(function (resolve, reject) {
if (read(1)) {
resolve("333");
} else {
reject("c fail");
}
});
} async function test() {
try {
let re_a = await readFileA();
let re_b = await readFileB();
let re_c = await readFileC();
console.log({re_a, re_b, re_c}); //如果都成功,return: { re_a: '111', re_b: '222', re_c: '333' }
} catch (err) {
console.log(err); // 如果b失败,return: b fail
}
} test();

总结:async / await 跟 callback 比的好处:

1、代码量最少,解决了回调地狱金字塔的缺陷(Promise 通过 then 链来解决 callback 多层回调金字塔的问题,现在又用 async/await 来进一步优化它)(基于 promise 的 async / await 也试图淘汰 promise)

2、.catch 可以统一处理错误(建议用不同的 Error 类作区分)


[拓展]

1、async 函数就是 Generator 函数的语法糖,本质上并不是同步代码

2、async 用于申明一个 function 是异步的,而 await (async wait) 用于等待一个异步方法执行完成。

3、await 只能出现在 async 函数中,所以在代码的顶层,我们无法使用 await,所以添加它 .then/catch 来处理最终结果或掉落错误是正常的做法。

try {
let re_a = await readFileA();
let re_b = await readFileB();
let re_c = await readFileC();
console.log({re_a, re_b, re_c});
} catch (err) {
console.log(err);
}

return:

报错

或者顶层使用立即执行函数表达式(IIFE)

(async () => {

    try {
let re_a = await readFileA();
let re_b = await readFileB();
let re_c = await readFileC();
console.log({re_a, re_b, re_c});
} catch (err) {
console.log(err);
} })()

return:

{ re_a: '111', re_b: '222', re_c: '333' }

上面的例子还可以这样写:

async function test() {
try {
let re_a = await readFileA();
let re_b = await readFileB();
let re_c = await readFileC();
console.log({re_a, re_b, re_c}); //如果都成功,return: { re_a: '111', re_b: '222', re_c: '333' }
} catch (err) {
console.log(err); // 如果b失败,return: b fail
}
} test().then(function(data){
console.log("success");
},function(err){
console.log("fail");
});

return:

{ re_a: '111', re_b: '222', re_c: '333' }
success

4、见上例,实际上 async 申明的 function 返回的就是一个 Promise 对象,这就是 await 必须用在 async 函数中的原因。async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。

区别是,async 申明的 function 里可以通过 return值 / 抛异常 来实现普通 Promise 的 resolve() / reject()

下面是对等关系:

// async 函数
async function foo () {
return 'a'
}
// Promise
function foo () {
return Promise.resolve('a')
}
// async 函数
async function foo () {
throw new Error('error')
}
// Promise
function foo () {
return Promise.reject(new Error('error'))
}

promise.all 实现需求B

async/await 同样适用于 Promise.all,因为 Promise.all 本身返回的就是 promise 对象。

let read = function (code) {
if (code) {
return true;
} else {
return false;
}
} let readFileA = function () {
return new Promise(function (resolve, reject) {
if (read(1)) {
resolve("111");
} else {
reject("a fail");
}
});
}
let readFileB = function () {
return new Promise(function (resolve, reject) {
if (read(1)) {
resolve("222");
} else {
reject("b fail");
}
});
}
let readFileC = function () {
return new Promise(function (resolve, reject) {
if (read(1)) {
resolve("333");
} else {
reject("c fail");
}
});
} async function test() {
try {
let re_a = await readFileA();
let re_b = await readFileB();
let re_c = await readFileC();
console.log({re_a, re_b, re_c});
} catch (err) {
console.log(err);
}
} async function test() {
try {
let results = await Promise.all([
readFileA(),
readFileB(),
readFileC(),
]);
console.log(results);
} catch (err) {
console.log(err);
}
} test();

参考资料

[使用 promises]

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises

[理解 JavaScript 的 async/await]

https://segmentfault.com/a/1190000007535316

[javascript.info-Async/await]

https://javascript.info/async-await#async-functions

callback vs async.js vs promise vs async / await的更多相关文章

  1. promise 进阶 —— async / await 结合 bluebird

    一.背景 1.Node.js 异步控制 在之前写的 callback vs async.js vs promise vs async / await 里,我介绍了 ES6 的 promise 和 ES ...

  2. 一个例子读懂 JS 异步编程: Callback / Promise / Generator / Async

    JS异步编程实践理解 回顾JS异步编程方法的发展,主要有以下几种方式: Callback Promise Generator Async 需求 显示购物车商品列表的页面,用户可以勾选想要删除商品(单选 ...

  3. node.js异步控制流程 回调,事件,promise和async/await

    写这个问题是因为最近看到一些初学者用回调用的不亦乐乎,最后代码左调来又调去很不直观. 首先上结论:推荐使用async/await或者co/yield,其次是promise,再次是事件,回调不要使用. ...

  4. Callback, Promise和Async/Await的对比

    Callback, Promise和Async/Await的对比 Callback Hell getData1(function (data1) { console.log('我得到data1了') ...

  5. Node.js 101(2): Promise and async

    --原文地址:http://blog.chrisyip.im/nodejs-101-package-promise-and-async 先回想一下 Sagase 的项目结构: lib/ cli.js ...

  6. callback、promise和async、await的使用方法

    callback 回调是一个函数被作为一个参数传递到另一个函数里,在那个函数执行完后再执行.通俗的讲就是 B函数被作为参数传递到A函数里,在A函数执行完后再执行B. promise Promise 是 ...

  7. setTimeout、Promise、Async/Await 的执行顺序

    问题描述:以下这段代码的执行结果 async function async1() { console.log('async1 start'); await async2(); console.log( ...

  8. Promise,Async,await简介

    Promise 对象 转载:http://wiki.jikexueyuan.com/project/es6/promise.html 基本用法 ES6 原生提供了 Promise 对象.所谓 Prom ...

  9. Promise及Async/Await

      一.为什么有Async/Await? 我们都知道已经有了Promise的解决方案了,为什么还要ES7提出新的Async/Await标准呢? 答案其实也显而易见:Promise虽然跳出了异步嵌套的怪 ...

随机推荐

  1. XSLT 创建CDATA节点

    创建文本结点 (1)直接写入文本: text1 (2)通过<xsl:text>创建文本结点: <xsl:text>text2</xsl:text> (3)通过< ...

  2. 安装好ubuntu双系统启动时卡死解决办法

    问题描述:在安装完ubuntu双系统后,第一次启动ubuntu系统时,卡死在启动界面(或者黑屏),这大概都是由于显卡驱动的原因,具体不在这里阐述,通过以下方法能成功解决,据我个人经验,这可能是诸多方法 ...

  3. 深度学习中,使用regularization正则化(weight_decay)的好处,loss=nan

    刚开始训练一个模型,自己就直接用了,而且感觉训练的数据量也挺大的,因此就没有使用正则化, 可能用的少的原因,我也就不用了,后面,训练到一定程度,accuracy不上升,loss不下降,老是出现loss ...

  4. Django提交文件的方式

    以上传图片为列, 提交图片文件后能在不刷新页面的前提下显示图片 首先写出一个简单的提交文件的页面: <h>文件上传</h> 选择文件<input type="f ...

  5. BlockTrain网络

    [BlockTrain网络] 1.每个节点都参与全⽹络的路由功能,同时也可能包含其他功能.每个节点都参与验证并传播交易及区块信息,发现并维持与对等节点的连接.在图6-1所⽰的全节点⽤例中,名为“⽹络路 ...

  6. extJs相关名字解释

      1.initComponent 初始化部件启动 2.defaults : Object defaults属性可以包含任意个name/value属性对,这些属性将会被添加到每一个元素中...例如, ...

  7. oracle优化技巧及实例(总结)

    1.关于exists和in in是循环的方式,在内存中处理, exists是执行数据库查询, select tpd.personaccountid,sum(nvl(tpd.CREDIT_SUM, 0) ...

  8. Linux系统minicom命令详解

    minicom 功能说明:调制解调器通信程序 语 法:minicom [-8lmMostz][-a<on或0ff>][-c<on或off>][-C<取文件>][-d ...

  9. Spring Boot 整合mybatis 使用多数据源

    本人想要实现一个项目里面多个数据库源连接,所以就尝试写一个demo,不多说,先贴结构,再贴代码,可以根据以下的顺序,直接copy解决问题. 首先,dao和resource下的mappers可以用myb ...

  10. nginx路径设置(web)

    原文 https://www.jianshu.com/p/57db2c5d0cb9 语法 root 语法:root path 默认值:root html 配置段:http.server.locatio ...