简单理解 Javascript 中的异步

异步是什么?

异步是相对于同步而言的,同步就是一行一行代码执行下去,异步呢就是分成多个任务同时执行。

举个例子:

西游记中大闹五庄观一回里,童子大骂唐僧师徒是偷果子的秃贼,孙悟空气的脸涨得通红,但又不能当师父面发作,于是真身出神,到园子里把人参果树推倒,没留一个果子,完事毫毛一收,趁着夜色解开门锁,带着师徒一行人逃跑了。

这里的猴子,本体还在挨骂受气呢,结果分身跑去铲果树了,这就是个异步的过程。

异步是怎么实现的?

javascript 不是单线程的吗,那么它是如何实现异步的呢?

如果你在加班的时候,外卖到了,你会怎么做呢?自己去拿,工作思路会被打断。像孙悟空一样分身,那不现实。好在公司不止一个人,我们可以叫其他同事帮忙拿一下,拿到外卖后告诉我们一声,然后我们就可以愉快的用餐了。

JS 也是如此,虽然它是单线程的,只能同时处理一件事,但是主线程可以发起一个异步请求,相应的工作线程就会接收这个请求并进行处理,等到工作线程的处理有了结果,浏览器就会通知主线程,刚才的请求已经完成,结果已经拿到了,可以执行回调函数,处理异步获得的结果。

如何编写异步代码?

最合理的方式是通过 Promise 对象。

举个例子,我们来看看它是怎么用的。

上个月小舅子向我借了点钱,今天偶然碰到他,我便问他什么时候能还钱,他说下个月发了工资就还我。我说不行啊,下个月我要出差。他说:这样吧,姐夫,你告诉我,你打算用这笔钱做什么,我替你去做。虽然我表示怀疑,但也只能接受了,毕竟我还有别的事情要去做,不可能一直等着他变出钱来。
于是我跟他说:好吧。那下个月你直接把欠我的钱,交到我的老婆那里好了。小舅子爽快的答应了。

把上面的情景用代码表示,就像这样:

// Promise 对象
let 小舅子的承诺 = new Promise((答应的事情) => {
    setTimeout(() => {
        let 工资 = 2000; // 发工资了
        let 要还的钱 = 工资 * 0.5; // 拿出一半还账
        答应的事情(要还的钱); // 处理姐夫的委托
    }, 30 * 100); // 30 天后...
});

// 回调函数
function 我的请求(一笔钱) {
    console.log('把' + 一笔钱 + '上交给老婆');
}

// then 指定回调函数
小舅子的承诺.then(我的请求); // 小舅子答应了我的请求

// 3秒后打印:把1000上交给老婆

async/await 是什么?

JS 的语法糖而已,可以让你从回调函数中解放出来。

还是以上面的情景为例,假如我打算拿这钱去做个按摩...像这种事绝对不能和小舅子说,要是我老婆要是知道了话,肯定会把我活剥了。那就让小舅子把要还的钱直接打我银行卡里,我就等银行的到账通知就行了。也不耽误我的工作,又不用告诉小舅子我的计划,而且我可以自由的安排资金的用途。

写成代码的话就像这样:

// Promise 对象
let 小舅子的承诺 = new Promise((答应的事情) => {
    setTimeout(() => {
        let 工资 = 2000; // 发工资了
        let 要还的钱 = 工资 * 0.5; // 拿出一半还账
        答应的事情(要还的钱); // 处理姐夫的委托
    }, 30 * 100); // 30 天后...
});

// 异步函数
async function 我的计划() {
    // 用了 await 委托就变成了:无需处理直接转账给我
    let 一笔钱 = await 小舅子的承诺; // 等待小舅子转账
    console.log('拿' + 一笔钱 + '去做个按摩');
};

// 调用异步函数
我的计划();

// 3秒后打印:拿1000去做个按摩

两种写法有什么区别吗?

都是基于 Promise 对象,只不过一个用 then 方法指定回调函数,一个用 await 获取 resolve 的值(其实就是参数本身)。需要注意的是 await 需要写在异步函数中(思考下,为什么)。

来做个小练习,可以帮你理解这二者之间的区别。

function get(url, callback) {
    var req = new XMLHttpRequest();
    req.open('GET', url, true);
    req.onload = function () {
        if (req.readyState == 4 && req.status == 200) {
            callback(req.response);
        }
    }
    req.send();
}

var url = 'https://miao.baidu.com/';

get(url, function (html) {
    var re = new RegExp('<[^<>]+>', 'g');
    var text = html.replace(re, "");
    console.log(text);
});

// 在 miao.baidu.com 页面的控制台中执行
// 打印:Hello World

请参照上面的代码,用 Promise 对象封装一个 get 函数。

通过这个函数访问 miao.baidu.com,并打印去除了所有 HTML 标签后的文本。

要求:使用 then 和 await 两种方法。

你可能感兴趣的