JavaScript 同步异步以及 Promise

发布于 2024-02-06 03:51:11 字数 3776 浏览 36 评论 0

在 JavaScript 的世界中,所有代码都是单线程执行的。

由于这个 缺陷,导致 JavaScript 的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现:

function callback() {
    console.log('Done');
}
console.log('before setTimeout()');
setTimeout(callback, 1000); // 1 秒钟后调用 callback 函数
console.log('after setTimeout()');

观察上述代码执行,在 Chrome 的控制台输出可以看到:

before setTimeout()
after setTimeout()
(等待 1 秒后)
Done

可见,异步操作会在将来的某个时间点触发一个函数调用。

AJAX 就是典型的异步操作。以上一节的代码为例:

request.onreadystatechange = function () {
    if (request.readyState === 4) {
        if (request.status === 200) {
            return success(request.responseText);
        } else {
            return fail(request.status);
        }
    }
}

把回调函数 success(request.responseText)fail(request.status) 写到一个 AJAX 操作里很正常,但是不好看,而且不利于代码复用。

有没有更好的写法?比如写成这样:

var ajax = ajaxGet('http://...');
ajax.ifSuccess(success)
    .ifFail(fail);

这种链式写法的好处在于,先统一执行 AJAX 逻辑,不关心如何处理结果,然后,根据结果是成功还是失败,在将来的某个时候调用 success 函数或 fail 函数。

古人云:“君子一诺千金”,这种“承诺将来会执行”的对象在 JavaScript 中称为 Promise 对象。

Promise 是 ES6 引入的异步编程的新解决方案。语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果

Promise 有各种开源实现,在 ES6 中被统一规范,由浏览器直接支持。我们先看一个最简单的 Promise 例子:生成一个 0-2 之间的随机数,如果小于 1,则等待一段时间后返回成功,否则返回失败:

function test(resolve, reject) {
    var timeOut = Math.random() * 2;
    console.log('set timeout to: ' + timeOut + ' seconds.');
    setTimeout(function () {
        if (timeOut < 1) {
            log('call resolve()...');
            resolve('200 OK');
        }
        else {
            log('call reject()...');
            reject('timeout in ' + timeOut + ' seconds.');
        }
    }, timeOut * 1000);
}

这个 test() 函数有两个参数,这两个参数都是函数,如果执行成功,我们将调用 resolve('200 OK') ,如果执行失败,我们将调用 reject('timeout in ' + timeOut + ' seconds.') 。可以看出, test() 函数只关心自身的逻辑,并不关心具体的 resolvereject 将如何处理结果。

有了执行函数,我们就可以用一个 Promise 对象来执行它,并在将来某个时刻获得成功或失败的结果:

var p1 = new Promise(test);
var p2 = p1.then(function (result) {
    console.log('成功:' + result);
});
var p3 = p2.catch(function (reason) {
    console.log('失败:' + reason);
});

Promise 对象可以串联起来,所以上述代码可以简化为:

new Promise(test).then(function (result) {
    console.log('成功:' + result);
}).catch(function (reason) {
    console.log('失败:' + reason);
});

需要注意的是,成功方法不仅可以使用 then-catch 分开写,还可以写在一起:

//第一个参数是成功时调用的函数,第二个参数是失败时调用的函数 
var p2 = p1.then(result=> {
    console.log('成功:' + result);
  },reason=>{
    console.log('失败' + reason);
  });

实际上 catch 方法就是一个语法糖,它就是调用 then 方法,然后第一个参数传 null。

then() 方法会返回一个的 Promise 实例,所以 then() 方法后面可以继续跟另一个 then() 方法进行链式调用。

let p = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000, 'success');
});
p.then(
    res => {
        console.log(res);
        return `${res} again`;
    }
).then(
    res => console.log(res)
);

实际上我们使用 return 直接返回结果,实际上返回的是一个 Promise 实例,是下面这种方式的简写:

return Promise.resolve(`${res} again`);

而这种方式又是下面的简写:

return new Promise(resolve => {
    return `${res} again`;
})

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

金兰素衣

暂无简介

0 文章
0 评论
22 人气
更多

推荐作者

qq_E2Iff7

文章 0 评论 0

Archangel

文章 0 评论 0

freedog

文章 0 评论 0

Hunk

文章 0 评论 0

18819270189

文章 0 评论 0

wenkai

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文