资讯专栏INFORMATION COLUMN

从js来聊聊异步编程

huashiou / 2642人阅读

摘要:实例生成以后,可以用方法分别指定状态和状态的回调函数处理返回的结果。

文章的目的

揭开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

通过这种方法就可以简单的实现异步了

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104296.html

相关文章

  • 聊聊 2018 年后端技术趋势

    摘要:现在在后端业务开发编程方面,技术力量强的团队已经开始将技术栈从同步模式切换为异步了。使用这些技术方案是无法兼容已有程序的。影响了异步回调技术栈的普及。将会成为未来后端开发领域的主流技术方案。 今天太忙,少写一点,后面再补充。 异步模式 Go 语言越来越热门,很多大型互联网公司后端正在转向 GO 。Java 圈知名的服务化框架 Dubbo 也宣布转型异步模式。这是一个大趋势,异步模式已经...

    Miyang 评论0 收藏0
  • 这次聊聊Promise对象

    摘要:异步模式编程有四种方法回调函数最基本的方法,把写成的回调函数事件监听为绑定事件,当发生某个事件,就执行发布订阅,以及本文要介绍的对象。它的思想是,每一个异步任务返回一个对象,该对象有一个方法,允许指定回调函数。 欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由前端林子发表于云+社区专栏 Promise是CommonJS提出的一种规范,在ES6中已经原生支持Promi...

    Eminjannn 评论0 收藏0
  • Javascript中的异步编程

    摘要:接下来,我们一起来看看中的异步编程,具体有哪几种。实现异步编程的方法一回调函数上面不止一次提到了回调函数。它是异步编程中,最基本的方法。四对象接下来,我们聊聊与相关的异步编程方法,对象。 showImg(https://segmentfault.com/img/bVbneWy?w=1600&h=1200); 前言 最近,小伙伴S 问了我一段代码: const funB = (value...

    wemall 评论0 收藏0
  • 2017-06-27 前端日报

    摘要:前端日报精选漫谈函数式编程一十年踪迹的博客前端每周清单的优势与劣势有望超越在嵌入式及物联网的应用现状进阶系列高阶组件详解一前端之路译如何充分利用控制台掘金程序猿升级攻略众成翻译中文译如何充分利用控制台掘金前端从强制开启压缩探 2017-06-27 前端日报 精选 漫谈 JS 函数式编程(一) - 十年踪迹的博客前端每周清单: Vue的优势与劣势;Node.js有望超越Java;JS在嵌...

    Eidesen 评论0 收藏0
  • 只为那句承诺-大话Promise

    摘要:什么是是异步编程的一种解决方案,比传统的解决方案回调函数和事件更合理和更强大。函数可以将状态转变为状态。对象通过方法来添加回调函数。当发生错误的时候可以通过方法,来定义回调函数。接受数组作为参数传入,每个元素都是一个对象。 大家周末好,要说最近几年什么语言大红大紫,当属JavaScript了。话说虽然是10天就创造出的语言,但是人家能文能武。web前端自然不必多说了,各种框架你方登罢我...

    EastWoodYang 评论0 收藏0

发表评论

0条评论

huashiou

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<