本文将会详细的向你介绍如何在JavaScript中借助Promise来简化异步代码流。
背景知识
JavaScript是单线程的,这意味着代码是按顺序执行的。对于浏览器而言,JavaScript代码和其他任务共享一个线程,
不同的浏览器略有差异,但大体上这些和JavaScript共享线程的任务主要包括重绘、更新样式、用户交互等,
所有这些任务操作都会阻塞其他任务。
避免事件阻塞的常用方法是使用事件监听器。我们可以为某些特定事件设置监听器,如果事件发生的话,便立刻触发监听器,
你应该已经习惯使用回调函数来解决这个问题了,例如:
1 2 3 4 5 6 7 8 |
var img1 = document.querySelector('.img-1'); img1.addEventListener('load', function() { // 图片加载完成 }); img1.addEventListener('error', function() { // 出问题了 }); |
上面的代码中,我们添加了两个监听器,请求图片,回调函数只在事件发生的时候才会被触发。但是通过事件机制还存在几个问题:
- 事件在绑定之前就发生了怎么办?
- 在添加监听器之前,图片加载发生了错误怎么办?
仅仅是一张图片就存在这么多问题,那么如果有一堆图片要处理,又该怎么办?下面我们就谈谈Promise,一个越来越流行的异步解决方案。
Promise
JavaScript的一大特点就是会涉及到大量的异步代码。同步代码通常易于理解和调试,而异步代码则具有更好的性能和灵活性。
目前Promise正逐渐称为JavaScript世界的一个重要组成部分,并且很多新的API也都基于Promise进行了实现。
目前已经有一些原生API使用了Promise,包括:
- Battery API
- Fetch API
- ServiceWorker API
什么是Promise
那么到底什么是Promise呢?Promise
是ES6规范新增的对象,它可以用于延迟计算和异步计算。
一个Promise
对象代表着一个还未完成,但预期会完成的操作。需要记住:
- 一个Promise要么成功要么失败,并且状态不可变
- 可以根据Promise的结果设置特定的回调函数
Promise的状态
一个Promise的状态可以是:
- 等待 pending – Promise的初始化状态,等待结果
- 完成 fullfilled – 该Promise对应的异步操作成功完成了
- 失败 rejected – 该Promise对应的异步操作失败了
- 结束 settled – 任务完成或失败了
基本使用
new Promise()
构造器应该只被用于传统的异步任务上,例如setTimeout
或XMLHttpRequest
。
通过new
关键字创建一个新的Promise
,它接收一个回调函数作为参数,该回调函数又包括了两个特定的回调函数,
分别被命名为resolve
和reject
,成功后调用resolve
,失败则调用reject
。
根据不同的任务,由开发者来决定resolve
和reject
在函数体内的位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
let p = new Promise(function(resolve, reject) { // 执行异步任务 if(/* good condition */) { resolve('Success'); } else { // 传递Error对象的好处是可以包含调用堆栈,便于调试 reject(Error('Failure')); } }); p.then(function(result) { // do something with the reuslt foo(result); }, function(err){ console.error(err); }); |
使用Promise则非常的简单,可以调用Promise
对象的then()
方法来处理异步计算的结果。then
接收两个回调函数,
分别是成功的回调函数和失败时的回调函数,这两个参数都是可选的。
Promise的使用有两点需要记住的:
then()
方法可以链式调用catch()
方法可以作为错误处理语句的语法糖,相当于then(undefined, function(error) { ... });
在具体讲解这两点之前,我们先来看一个例子。下面这个例子用于将XMLHttpRequest
转换为一个基于Promise的接口。
我们以GET请求为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
function get(url) { // 返回一个新的 Promise return new Promise(function(resolve, reject) { // 经典 XHR 操作 var req = new XMLHttpRequest(); req.open('GET', url); req.onload = function() { // 当发生 404 等状况的时候调用此函数 // 所以先检查状态码 if (req.status == 200) { // 以响应文本为结果,完成此 Promise resolve(req.response); } else { // 否则就以状态码为结果否定掉此 Promise // (提供一个有意义的 Error 对象) reject(Error(req.statusText)); } }; // 网络异常的处理方法 req.onerror = function() { reject(Error("Network Error")); }; // 发出请求 req.send(); }); } |
我们现在可以这么调用它:
1 2 3 4 5 |
get('story.json').then(function(response) { console.logfunction(response) { console.log>
不同的浏览器略有差异,但大体上这些和JavaScript共享线程的任务主要包括重绘、更新样式、用户交互等, 所有这些任务操作都会阻塞其他任务。 避免事件阻塞的常用方法是使用事件监听器。我们可以为某些特定事件设置监听器,如果事件发生的话,便立刻触发监听器,
上面的代码中,我们添加了两个监听器,请求图片,回调函数只在事件发生的时候才会被触发。但是通过事件机制还存在几个问题:
仅仅是一张图片就存在这么多问题,那么如果有一堆图片要处理,又该怎么办?下面我们就谈谈Promise,一个越来越流行的异步解决方案。 PromiseJavaScript的一大特点就是会涉及到大量的异步代码。同步代码通常易于理解和调试,而异步代码则具有更好的性能和灵活性。
什么是Promise那么到底什么是Promise呢?
Promise的状态一个Promise的状态可以是:
基本使用
根据不同的任务,由开发者来决定
使用Promise则非常的简单,可以调用 Promise的使用有两点需要记住的:
在具体讲解这两点之前,我们先来看一个例子。下面这个例子用于将
我们现在可以这么调用它:
|