callback vs async.js vs promise vs async / await

需求:

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()

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

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 事件监听

1\事件对于同一对象上发生多次的事情(如 keyup、touchstart 等)

通过 .then 形式添加的回调函数,甚至都在异步操作完成之后才被添加的函数,都会被调用,如上所示。

2\如果 promise 已成功或失败,且您之后添加了成功/失败回调,则将会调用正确的回调,即使事件发生在先。

3\对某些功能可用的准确时间不是那么关注,更多地是关注对结果作出的反应。

4\甚至都在异步操作完成之后才被添加的函数,都会被调用



promise 扩展知识

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‘ }

return:

TypeError: (intermediate value)(...) is not a function


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

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

原文地址:https://www.cnblogs.com/xjnotxj/p/9477987.html

时间: 2024-10-10 05:57:00

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

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

JS异步编程实践理解 回顾JS异步编程方法的发展,主要有以下几种方式: Callback Promise Generator Async 需求 显示购物车商品列表的页面,用户可以勾选想要删除商品(单选或多选),点击确认删除按钮后,将已勾选的商品清除购物车,页面显示剩余商品. 为了便于本文内容阐述,假设后端没有提供一个批量删除商品的接口,所以对用户选择的商品列表,需要逐个调用删除接口. 用一个定时器代表一次接口请求.那思路就是遍历存放用户已选择商品的id数组,逐个发起删除请求del,待全部删除完成

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

写这个问题是因为最近看到一些初学者用回调用的不亦乐乎,最后代码左调来又调去很不直观. 首先上结论:推荐使用async/await或者co/yield,其次是promise,再次是事件,回调不要使用. 接下来是解析,为什么我会有这样的结论 首先是回调,理解上最简单,就是我把任务分配出去,当你执行完了我就能从你那里拿到结果执行相应的回调, 这里演示一个对setTimeout的封装,规定时间后打印相应结果并执行回调函数 并且这个函数传给回调函数的参数符合node标准,第一个为error信息,如果出错e

Node.js 101(2): Promise and async

--原文地址:http://blog.chrisyip.im/nodejs-101-package-promise-and-async 先回想一下 Sagase 的项目结构: lib/ cli.js sagase.js Gruntfile.js package.json 上一篇讲了 package.json,这一篇讲 lib/sagase.js. 由于代码比較长,就分开一节节地讲,完整的点开 GitHub 看吧. 'use strict'; 通知编译器进入 strict mode,基本的作用是让

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

callback 回调是一个函数被作为一个参数传递到另一个函数里,在那个函数执行完后再执行.通俗的讲就是 B函数被作为参数传递到A函数里,在A函数执行完后再执行B. promise Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大,ES6规定,Promise对象是一个构造函数,用来生成Promise实例.Promise实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的. async/await 它就是

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

问题描述:以下这段代码的执行结果 async function async1() { console.log('async1 start'); await async2(); console.log('asnyc1 end'); } async function async2() { console.log('async2'); } console.log('script start'); setTimeout(() => { console.log('setTimeOut'); }, 0);

Promise,Async,await简介

Promise 对象 转载:http://wiki.jikexueyuan.com/project/es6/promise.html 基本用法 ES6 原生提供了 Promise 对象.所谓 Promise 对象,就是代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理. 有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数.此外,Promise 对象提供的接口,使得控制异步操作更加容易.Promise

Async.js解决Node.js操作MySQL的回调大坑

因为JavaScript语言异步特性.在使用Node.js运行非常多操作时都会使用到回调函数,当中就包含訪问数据库.假设代码中的业务逻辑略微复杂一点,回调一层层嵌套.那么代码非常easy进入Callback Hell,不管对写代码的人还是阅读代码的人,都是精神上的折磨. 比如对MySQL的一个事务操作,插入一条posts并插入一条log: var title = 'It is a new post'; connection.beginTransaction(function(err) { if

async.js原理解析

async.js 原理解析 暂时只是针对常用的Waterfall方法 做node开发,你不能逃避的一个组件:async.js. 神奇之处在于让你用看上去同步的方式写出异步的代码,把我们从各种回调嵌套中解脱出来,程序逻辑一目了然.以下简要对async的each和waterfall方法的实现原理做介绍. async.each 先看使用方法 async.waterfall([ function(callback){ callback(null, 'one', 'two'); }, function(a

重构:从Promise到Async/Await

摘要: 夸张点说,技术的发展与历史一样,顺之者昌,逆之者亡.JS开发者们,赶紧拥抱Async/Await吧! GitHub仓库: Fundebug/promise-asyncawait 早在半年多之前,我就在鼓吹Async/Await替代Promise的6个理由,似乎还招致了一些批评.然而,直到最近,我才真正开始进行代码重构,抛弃Promise,全面使用Async/Await.因为,Node 8终于LTS了! Async/Await真的比Promise好吗? 是的是的. 这些天,我大概重构了10