【ES6】Promise 使用

文章目录

一、含义

1、是什么

Promise 是异步编程的一种解决方案

2、特点

Promise对象有以下两个特点:

1、对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)

2、一旦状态改变,就不会再变,任何时候都可以得到这个结果

二、用法

1、基本使用

ES6 规定,Promise对象是一个构造函数,用来生成Promise实例

代码示例:

js 复制代码
const promise = new Promise(function (resolve, reject) {
  // ... some code
  console.log('这里的代码是同步代码,会立即执行~');

  let flag = true;
  // let flag = false;
  if (flag) {
    resolve(true);
  } else {
    reject(false);
  }
});

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数:

js 复制代码
promise.then(
  function (value) {
  	// success
  	// Promise对象的状态变为resolved时调用
  },
  function (error) {
    // failure
    // Promise对象的状态变为rejected时调用
  }
);

示例:timeout

js 复制代码
function timeout(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('done');
    }, time);
  });
}

timeout(2000).then((value) => {
  console.log(value);
});

示例:异步加载图片

js 复制代码
function loadImageAsync(url) {
  return new Promise(function(resolve, reject) {
    const image = new Image();

    image.onload = function() {
      resolve(image);
    };

    image.onerror = function() {
      reject(new Error('Could not load image at ' + url));
    };

    image.src = url;
  });
}

2、resolve函数和reject函数带有参数

如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。

2.1 resolve

2.1.1 参数为正常值的情况
js 复制代码
const p = new Promise((resolve, reject) => {
  resolve('hello');
});

p.then((value) => {
  console.log(value); // hello
});
2.1.2 参数为Promise 实例的情况

当一个 Promise 实例在 resolve 方法中返回另一个 Promise 实例时,当前 Promise 的最终状态将由内部 Promise 实例的状态决定

如下示例:

p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态

如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行

js 复制代码
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('这是p1的resolve');
    // reject('这是p1的reject');
  }, 1000);
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // p2 resolve传递 p1 实例
    resolve(p1);
  }, 1000);
});

p2.then((value) => {
  console.log('resolve--', value); // resolve--这是p1的resolve
}).catch((error) => {
  console.log('reject--', error);
});

2.2reject

reject函数的参数通常是Error对象的实例,表示抛出的错误

js 复制代码
const p = new Promise((resolve, reject) => {
  reject(new Error('出错了'));
});

p.catch((error) => {
  console.log('报错', error); // 报错Error{}
});

注意,调用resolve或reject并不会终结 Promise 的参数函数的执行。

js 复制代码
new Promise((resolve, reject) => {
  resolve(1);
  console.log(2); // 这里仍然会执行
}).then(r => {
  console.log(r);
});
// 2
// 1

3、Promise.prototype.then()

(1)then方法参数:

  • 第一个参数是 resolved 状态的回调函数(可选的)
  • 第二个参数是 rejected 状态的回调函数(可选的)

(2)then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例),因此可以采用链式写法

js 复制代码
getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // ...
});

(3)当前then里面的回调函数返回的还是一个Promise对象(即有异步操作)时,这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用

js 复制代码
function getCount(count) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(count + 1);
    }, 2000);
  });
}

getCount(1)
  .then((val) => {
    console.log(val); // 2
    // 返回的还是一个Promise对象
    return getCount(val);
  })
  .then((val) => {
    console.log(val); // 3
  });

4、Promise.prototype.catch()

(1)Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

js 复制代码
getJSON('/posts.json').then(function(posts) {
  // ...
}).catch(function(error) {
  // 处理 getJSON 和 前一个回调函数运行时发生的错误
  console.log('发生错误!', error);
});

(2)如果 Promise 状态已经变成resolved,再抛出错误是无效的。

js 复制代码
const promise = new Promise(function(resolve, reject) {
  resolve('ok');
  throw new Error('test'); // 无效
});
promise
  .then(function(value) { console.log(value) })
  .catch(function(error) { console.log(error) });
// ok

(3)Promise 对象的错误具有"冒泡"性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

js 复制代码
getJSON('/post/1.json').then(function(post) {
  return getJSON(post.commentURL);
}).then(function(comments) {
  // some code
}).catch(function(error) {
  // 处理前面三个Promise产生的错误
});

5、Promise.prototype.finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作

js 复制代码
promise
	.then(result => {···})
	.catch(error => {···})
	.finally(() => {···});

6、Promise.all()

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例

js 复制代码
const p = Promise.all([p1, p2, p3]); // p1、p2、p3都是 Promise 实例

p的状态由p1、p2、p3决定,分成两种情况:

(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数

示例:

js 复制代码
const p1 = new Promise(function (resolve, reject) {
  resolve(1);
});
const p2 = new Promise(function (resolve, reject) {
  resolve(2);
});
const p3 = new Promise(function (resolve, reject) {
  resolve(3);
});

const p = Promise.all([p1, p2, p3]);
p.then((list) => {
  console.log(list); // [1, 2, 3]
});

7、Promise.resolve()

将现有对象转为 Promise 对象

js 复制代码
Promise.resolve('foo')

等价于:

js 复制代码
new Promise(resolve => resolve('foo'))

8、Promise.reject()

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

js 复制代码
const p = Promise.reject('出错了');

等同于

js 复制代码
const p = new Promise((resolve, reject) => reject('出错了'))

三、练习题

1、第1题

js 复制代码
new Promise((resolve, reject) => {
  console.log(1);
  resolve();
})
  .then(() => {
    console.log(2);

    return new Promise((resove) => {
      console.log(4)
      resove();
    })
      .then(() => {
        console.log(5);
      })
      .then(() => {
        console.log(6);
      });
  })
  .then(() => {
    console.log(3);
  });

解析:

(1)外部第一个 new Promise 执行,执行完 resolve ,然后执行外部第一个 then

(2)外部第一个 then 方法里面 return 一个 Promise,这个 return 代表 外部的第二个 then 的执行需要等待 return 之后的结果

(3)执行完内部两个 then 之后,再执行 外部的第二个 then

2、第2题

注意:和第一题相差一个 return

js 复制代码
// 外部 promise
new Promise((resolve, reject) => {
  console.log(1);
  resolve();
})
  .then(() => {
    // 外部第一个 then
    console.log(2);
	
	// 内部 promise
    new Promise((resove) => {
      console.log(4);
      resove();
    })
      .then(() => {
      	// 内部第一个then
        console.log(5);
      })
      .then(() => {
      	// 内部第二个then
        console.log(6);
      });
  })
  .then(() => {
    // 外部第二个then
    console.log(3);
  });

解析:

重点: 下一个 then 的注册,需要等待上一个 then 的同步代码执行完成

执行顺序:

  1. 外部的 promise 立即执行,打印1;外部 promise 进行 resolve 后,执行外部第一个 then 的注册,外部第二个 then 进入等待状态
  2. 执行外部第一个 then, 打印2 ;立即执行内部的 promise 打印4;进行 resolve后,内部的第一个 then 注册,内部的第二个 then 进入等待状态;此时外部的第一个 then 的同步操作已经完成,然后开始注册外部第二个 then
  3. 执行内部第一个 then (因为内部第一个then是比外部第一个then先注册的),打印5,然后注册内部第二个 then;
  4. 执行外部第二个 then,打印3
  5. 执行内部第二个 then,打印6

所以最终的顺序:

js 复制代码
1
2
4
5
3
6

3、第3题

注意:内部第二个then不是链式调用

js 复制代码
// 外部 promise
new Promise((resolve, reject) => {
  console.log(1);
  resolve();
})
  .then(() => {
  	// 外部第一个 then
    console.log(2);
    
	// 内部 promise
    let p = new Promise((resolve, reject) => {
      console.log(4);
      resolve();
    });
    // 内部第一个then
    p.then(() => {
      console.log(5);
    });
    // 内部第二个then,注意这里不是链式调用,这里是跟内部第一个then是同步注册的
    p.then(() => {
      console.log(6);
    });
  })
  .then(() => {
  	// 外部第二个 then
    console.log(3);
  });

解析:

执行内部的 Promise 的 resolve 执行之后,两个同步 p.then 是两个执行代码语句,都是同步执行,自然是会同步注册完。

输出结果:

js 复制代码
1
2
4
5
6
3

4、第4题

js 复制代码
let p = new Promise((resolve, reject) => {
  console.log(1);
  resolve();
});

p.then(() => {
  console.log(2);

  new Promise((resolve, reject) => {
    console.log(3);
    resolve();
  })
    .then(() => {
      console.log(5);
    })
    .then(() => {
      console.log(6);
    });
});

p.then(() => {
  console.log(4);
});

解析:

外部的注册采用了非链式调用的写法,因此外部的 p.then 是并列同步注册的

内部的第一个 then 注册之后,就开始执行外部的第二个 then 了。然后再依次执行内部的第一个 then ,内部的第二个 then

5、第5题

js 复制代码
new Promise((resolve, reject) => {
  console.log(1);
  resolve();
})
  .then(() => {
    console.log(2);

    new Promise((resolve, reject) => {
      console.log(4);
      resolve();
    })
      .then(() => {
        console.log(5);
      })
      .then(() => {
        console.log(6);
      });
      
    return new Promise((resolve, reject) => {
      console.log(7);
      resolve();
    })
      .then(() => {
        console.log(8);
      })
      .then(() => {
        console.log(9);
      });
  })

  .then(() => {
    console.log(3);
  });

解析:

核心:外部的第二个 then ,依赖于内部的 return 的执行结果,所以会等待 return 执行完成

最终输出:

js 复制代码
1
2
4
7
5
8
6
9
3
相关推荐
轻口味1 小时前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王1 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发2 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀2 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪2 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
ekskef_sef4 小时前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine6414 小时前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻5 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云5 小时前
npm淘宝镜像
前端·npm·node.js