JavaScript 同步异步以及 Promise
在 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()
函数只关心自身的逻辑,并不关心具体的 resolve
和 reject
将如何处理结果。
有了执行函数,我们就可以用一个 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 技术交流群。
上一篇: ES6 生成器
下一篇: 谈谈自己对于 AOP 的了解
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论