js异步和网络Api发展史

从ajax到fetch,从回调到async,滑稽带你领略js异步和网络Api发展史。
仅仅是速览不会深入

js是事件驱动的语言,单进程单线程。js主线程中有一个Event Loop(事件循环)。

js运行顺序

js先执行一遍所有js文件,然后进入Event Loop,等到有事件进入,就执行事件处理程序,执行完后再次回到Event Loop

举个例子:

setTimeout(()=>{
  console.log("hello");
},0);
//死循环
while(true){
}

如果你在浏览器中运行,页面会卡死(在一些浏览器中仍可以滚动),因为js不执行完,浏览器不会做任何响应。

上面的代码永远不会输出hello,因为setTimeout(...,0),虽然是延时0ms,但还是会在下一个事件循环中被调用。

而后面的while循环是死循环,js引擎永远执行不完,所以不会进入事件循环的,也就永远执行不到timeout里的代码

更详细的介绍可在阮老师的博客中看见。

异步

所以啥是异步呢?

我们先讲异步的反义词同步。

同步就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。

比如你发出一个网络请求请求一个数据,程序一直等待数据返回,不返回就不继续执行,这就是同步。

异步要智能得多。在网络请求没有返回之前程序还会继续执行。

延迟一秒在不同语言中同步、异步的不同写法:

JS 异步

console.log(1);
setTimeout(()=>{
  console.log(2);
},1000);
console.log(3);
//输出 1 3 2

Java 同步

System.out.print(1);
//延迟1000ms
Thread.sleep(1000);
System.out.print(2);
System.out.print(3);
//输出 1 2 3

显然Java的同步跟符合人类的思想,顺序执行非常简单。

JS的优势也特别明显,在延迟时程序不会卡死,而是继续执行下去。

回调(callback)

回调是JS异步编程最常见的方式。

回调函数(callback)是什么?(逼乎)

function callback(){
  console.log("hello");
}

setTimeout(callback,1000);

上面的callback()就作为回调函数,在1000ms后会被调用。

Ajax

回到我们的主题,运用最广泛的网络api ajax就是以回调方式使用的。

var xmlhttp;
xmlhttp=new XMLHttpRequest();
//定义回调函数
xmlhttp.onreadystatechange=function()
{
    //如果请求完成 且 返回状态码为200
    if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
        //输出内容
        console.log(xmlhttp.responseText);
    }
}
xmlhttp.open("GET","https://about.huaji8.top/links.json",true/*异步运行*/);
//发送
xmlhttp.send();

复制到浏览器console可以运行。

jquery封装的ajax写法要比原版简单的多,对这是原版。

ajax也可以同步运行,只要将XMLHttpRequest.open()最后一个参数改成false,就是同步运行,执行send()后浏览器会卡住,直到请求返回。

Promise (承诺)

Promise是一种更高级的回调接口。

详细的介绍在这ECMAScript 6 入门Promise的好处千千万,建议大家全部使用Promisejquery的所有异步api也全部支持Promise

ES6 原生支持 Promise , 在不支持的浏览器上可以使用垫片库 es6-promise

下面是一个将setTimeout()改造成Promise的例子。

//返回一个Promise 就可以无限then()啦 还可以用catch()捕获异常
function setTimeoutP(ms){
    //创建Promise 如果成功就调用resolve,失败就调用reject
    return new Promise((resolve,reject)=>{
        setTimeout(resolve,ms);
    })
}

setTimeoutP(1000).then(()=>{
    console.log('Hello');
    // 返回Promise 可以在then()中继续执行
    return setTimeoutP(1000);
}).then(()=>{
    console.log('world');
    // 返回数据 可以在then()中获取
    return 123;
}).then((r)=>{
    console.log(r);
})
//一秒后输出Hello

fetch

fetch 是新的网络api 基于 Promise 设计。在旧的浏览器上可以使用 isomorphic-fetch,这个api是 node 和 浏览器环境通用的,实现同构应用必备的库。

注:同构(isomorphic/universal)就是使前后端运行同一套代码的意思,后端一般是指 NodeJS 环境。

范例

fetch("https://about.huaji8.top/links.json")
    .then((e)=>{return e.json()})
    .then((e)=>{console.log(e)});

尝试在浏览器console中运行

Generator 函数

Generator 函数ES6 提供的语法,不过很快就被 ES2017async 函数 代替,所以不多说了,感兴趣的可以看ECMAScript 6 入门

第一次看到 Generator 函数 是在 unity 中的协程,现在js也有这个语法了。

async 函数

重头来了,这是目前最屌的写法,真正用同步的写法写异步程序。

async 实际是 Generator 函数 的语法糖, Generator 才是技术

我们继续拿上面建的 setTimeoutP() 举例。

//返回一个Promise
function setTimeoutP(ms){
    //创建Promise 如果成功就调用resolve,失败就调用reject
    return new Promise((resolve,reject)=>{
        setTimeout(resolve,ms);
    })
}

async function asyncDelay(){
    // await可以拿到Promise的返回值
    let O = await fetch("https://about.huaji8.top/links.json")
        .then((e)=>{return e.json()})
    await setTimeoutP(1000)
    console.log(O);
    //一秒后输出 https://about.huaji8.top/links.json 内容
}

asyncDelay();

尝试在浏览器console中运行

async 异常处理

async 可以以同步的方式编写异常处理。

async function asyncDelay(){
    try{
        let O = await fetch("https://about.huaji8.top/links.json")
            .then((e)=>{return e.json()})
    }catch(e){
        //输出异常
        console.log(e)
        ....
    }
    await setTimeoutP(1000)
    console.log(O);
}

是不是超级方便呢。

关于async的更多信息,可以查看ECMAScript 6 入门MDN


Previous
Hello World Hello World
Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hex
2017-03-27
Next
性格职业测试app 性格职业测试app
项目介绍1. 项目背景 2. 项目意义 3. 总体设计 4. 项目源代码 1. 项目背景在当今这个信息大爆炸的时代 , 各个行业都迅猛发展 ,传达给人们的信息量越来越大, 形形色色的行业 的
2017-03-12
目录