从js来聊聊异步编程
栏目: JavaScript · 发布时间: 5年前
内容简介:揭开go的 gorouter,c#的 async/await等 使用同步的写法写异步代码的神秘面纱 , 证明其本质就是一个语法糖因为js可以通过编程语言自己的语法特性,实现async/await语法promise出入的回调函数有一定的要求
文章的目的
揭开 go 的 gorouter,c#的 async/await等 使用同步的写法写异步代码的神秘面纱 , 证明其本质就是一个语法糖
为什么使用js来讲异步编程
因为js可以通过编程语言自己的语法特性,实现async/await语法
js异步最底层写法promise
const promise = new Promise(function(resolve, reject) { xxxxx.异步IO操作((res)=>{ if(res成功){ resolve(res) }else{ reject(res) } }) });
promise出入的回调函数有一定的要求
- resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
- reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数(处理返回的结果)。
promise.then(function(value) { // success }, function(error) { // failure });
引申-注意: promise对象在js中非常特殊,比如下面的例子
const p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('fail')), 3000) }) const p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000) }) p2 .then(result => console.log(result)) .catch(error => console.log(error))
这个的结果是failt 因为 p2中resolve返回一个promise对象,这个操作将会导致p2的状态升级成p1的状态(标准)
promise的then链式写法
promise then方法将会返回一个promise,所以js支持链式异步
var getJSON = function (url, callback) { var promise = new Promise(function (resolve, reject) { var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler;//readyState属性的值由一个值变为另一个值时,都会触发readystatechange事件 client.responseType = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler() { if (this.readyState !== 4) { return; } if (this.status === 200) { callback(this.response); resolve(this.response); } else { reject(new Error(this.statusText)) } }; }); return promise; }; getJSON("./e2e-tests/get.json", function (resp) { console.log("get:" + resp.name); }).then(function (json) { getJSON("./e2e-tests/get2.json", function (resp) { console.log("get2:" + resp.name); }) }).catch(function (error) { console.log("error1:" + error); });
promise 异常捕获
p.then((val) => console.log('fulfilled:', val)) .catch((err) => console.log('rejected', err)); // 等同于 p.then((val) => console.log('fulfilled:', val)) .then(null, (err) => console.log("rejected:", err));
这个异常捕获和 java 相同,捕获在eventLoop中产生的异常
注意一点这个异常和java的try catch是不同的,如果产生了异常将不会在主线程中显示出来
promise的finally
这个和java的异常体系相同,finally 无关状态,最后都会执行
Promise.resolve(2).finally(() => {})
更加方便的编写异步使用Promise.resolve(xxx)
Promise.resolve('foo') // 等价于 new Promise(resolve => resolve('foo'))
注意: promise异步化结果只能在回调函数中获得,如果异步的操作太多,将会调至调用链路过长
如何解决js的promise异步编程的问题?
promise 写法有什么问题? ---- 调用链路过长
比如: 使用promise 实现 异步ajax请求
var getJSON = function (url, callback) { var promise = new Promise(function (resolve, reject) { var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler;//readyState属性的值由一个值变为另一个值时,都会触发readystatechange事件 client.responseType = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler() { if (this.readyState !== 4) { return; } if (this.status === 200) { callback(this.response); resolve(this.response); } else { reject(new Error(this.statusText)) } }; }); return promise; }; getJSON("./e2e-tests/get.json", function (resp) { console.log("get:" + resp.name); }).then(function (json) { getJSON("./e2e-tests/get2.json", function (resp) { console.log("get2:" + resp.name); }) }).catch(function (error) { console.log("error1:" + error); });
调用链太长,不停的promise调用
js如何解决回调地狱---同步方法写异步
解决方法 使用js的协程 --Generator
generator:js的特殊语法,使用yield 关键字将函数分块了,然后可以使用遍历器手动控制执行
例子:
function * gen(){ let a= 123; let b = yield a; let c = yield a+b; return a+b+c; } let start = gen(); console.log(start.next()); console.log(start.next(2)); console.log(start.next(3));
本质上是函数分片
js在每次yield的时候都会获得当前位置的表达式,然后再手动的嵌入就可以实现分片控制的效果了
怎么用generator实现异步化呢 -- yield配合promise实现异步
看一下这个方法
function* asyncFn(value) { let a = yield promiseOne(value); let b = yield promiseTwo(a); return a + b; }
想让他能异步执行,只要能让前一个promise的结果是下一个promise的输入就可以了
这里有两种写法
写法一
递归方程: f(最终结果) = f(到目前的结果)+f(接下来执行的结果)
function promiseOne(xxx) { return new Promise((res, rej) => { res(xxx + 1); }) } function promiseTwo(xxx) { return new Promise((res, rej) => { res(xxx + 1); }) } function* asyncFn(value) { let a = yield promiseOne(value); let b = yield promiseTwo(a); return a + b; } function runAsync(fn,value) { let item = fn.next(value); return new Promise((res, rej) => { if (!item.done) { if (item.value instanceof Promise) { item.value.then((re)=>{ runAsync(fn,re).then(res); }) } else { runAsync(fn,fn.valueOf()).then(res); } } else { res(item.value);//这个res方法其实是所有人的res方法 } }) } runAsync(asyncFn(12)).then(res=>{ console.log(res); });
co 工具包的写法
function run (gen) { gen = gen() return next(gen.next()) function next ({done, value}) { return new Promise(resolve => { if (done) { // finish resolve(value) } else { // not yet value.then(data => { next(gen.next(data)).then(resolve) }) } }) } } function getRandom () { return new Promise(resolve => { setTimeout(_ => resolve(Math.random() * 10 | 0), 1000) }) } function * main () { let num1 = yield getRandom() let num2 = yield getRandom() return num1 + num2 } run(main).then(data => { console.log(`got data: ${data}`); })
写法二
递归方程 f(最终结果) = f(之前所有的结果)+f(最后一步的结果)
//同步方式写异步 function asyncRun(resf, fn, value) { let a = fn(value); go(value); function go(value) { let next = a.next(value); if (!next.done) { if (next.value instanceof Promise) { next.value.then((res) => { go(res); }); } else { return go(next.value); } } else { resf(next.value); } } } function* asyncFn(value) { let a = yield promiseOne(value); let b = yield promiseTwo(a); return a + b; } function show(item) { console.log(item) } asyncRun(show, asyncFn, 12); function promiseOne(xxx) { return new Promise((res, rej) => { res(xxx + 1); }) } function promiseTwo(xxx) { return new Promise((res, rej) => { res(xxx + 1); }) }
更简单的方法 async/await
上面复杂的代码如果变成async/await要怎么做呢
很简单
// function* asyncFn(value) { // let a = yield promiseOne(value); // let b = yield promiseTwo(a); // return a + b; // } function promiseOne(xxx) { return new Promise((res, rej) => { res(xxx + 1); }) } function promiseTwo(xxx) { return new Promise((res, rej) => { res(xxx + 1); }) } async function asyncFn(value) { let a = await promiseOne(value); let b = await promiseTwo(a); return a + b; } asyncFn(12).then((res)=>{ console.log(res) });
通过上面的例子,我们可以发现其实async/await本质上其实是 generator的一个语法糖
await就是yield , async 的作用就是将函数编程语法糖
如果背的话很简答两条规则:
- await后面必须是promise函数
- async 标记过得函数执行后返回的promise
通过这种方法就可以简单的实现异步了
以上所述就是小编给大家介绍的《从js来聊聊异步编程》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- SpringBoot | :异步开发之异步调用
- 改进异步封装:处理带返回值的异步调用
- 异步发展流程 —— Generators + co 让异步更优雅
- 文件系统与异步操作——异步IO那些破事
- js异步从入门到放弃(四)- Generator 封装异步任务
- netty的Future异步回调难理解?手写个带回调异步框架就懂了
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Approximation Algorithms
Vijay V. Vazirani / Springer / 2001-07-02 / USD 54.95
'This book covers the dominant theoretical approaches to the approximate solution of hard combinatorial optimization and enumeration problems. It contains elegant combinatorial theory, useful and inte......一起来看看 《Approximation Algorithms》 这本书的介绍吧!