参考链接:
理解 Javascript 中的 Promise
菜鸟教程
https://www.runoob.com/w3cnote/javascript-promise-object.html

0、背景

js是一种同步编程语言,但是由于回调函数,我们可以使它像异步编程语言一样工作

1、概述

  • 是异步编程的一种解决方案。
  • 从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。
  • Promise 对象表示异步操作的最终完成(或失败)及其结果值

2、Promise的状态

Promise 异步操作有三种状态:

  • pending(进行中)
  • fulfilled(已成功)
  • rejected(已失败)。
    除了异步操作的结果,任何其他操作都无法改变这个状态。

Promise 对象只有:

  • 从 pending 变为 fulfilled
  • 从 pending 变为 rejected 的状态改变。
  • 只要处于 fulfilled 和 rejected ,状态就不会再变了即 resolved(已定型)。
const p1 = new Promise(function(resolve,reject){
    resolve('success1');
    resolve('success2');
}); 
const p2 = new Promise(function(resolve,reject){  
    resolve('success3'); 
    reject('reject');
});
//只会执行第一个resolve
p1.then(function(value){  
    console.log(value); // success1
});
p2.then(function(value){ 
    console.log(value); // success3
});

状态的缺点

  • 无法取消 Promise ,一旦新建它就会立即执行,无法中途取消。
  • 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
  • 当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

3、Promise的创建

  • 构造函数接受一个名为executor 的函数。
  • 此执行函数接受两个参数 resolve 和 reject,它们都是函数。
  • Promise 通常用于更容易处理异步操作或阻塞代码,其示例包括文件操作,API调用,DB调用,IO调用等。这些异步操作的启动发生在执行函数中。
  • 如果异步操作成功,则通过 promise 的创建者调用resolve 函数返回预期结果,同样,如果出现意外错误,则通过调用 reject 函数传递错误具体信息。

同步

//这个promise会立即执行,并不是异步操作,无法检查该promise的初始状态
var keepsHisWord;
keepsHisWord = true;
//参数resolve和reject都是函数
promise1 = new Promise(function(resolve, reject) {
  if (keepsHisWord) {
     //操作成功--resolve
    resolve("小智承诺坚持分享好的东西给大家");
  } else {
    //操作失败--reject
    reject("我没有做到!");
  }
});
console.log(promise1);

创建一个异步的promise----最常见的方法是用setTimeOut

promise2 = new Promise(function(resolve, reject) {
  //10s后promise会被执行
  setTimeout(function() {
    resolve({
      message: "小智承诺坚持分享好的东西给大家",
      code: "200"
    });
  }, 10 * 1000);
});
console.log(promise2);

10s前
图片说明
10s后
图片说明

图片说明

PromiseStatus

  • 创建 promise 时,PromiseStatus 将处于 pending 状态,并且 PromiseValue 为 undefined
  • 直到 promise 被 resolved 或 rejected 为止。
  • 当 promise 处于 resolved 或 rejected 的状态时,就称为 settled(已定型)。 所以 promise 通常从 pending 态转换到 settled 状态。

4.then方法

then方法接受两个函数作为参数,第一个参数是promise执行成功时的回调,第二个参数是promise执行失败时的回调,两个函数只会有一个被调用

特点:

  • 在js事件队列的当前运行完成之前,回调函数永远不会被调用
    const p = new Promise(function(resolve,reject){
    resolve('success');
    });
    p.then(function(value){
    console.log(value);
    });
    console.log('first');
    // first
    // success
  • 通过.then形式添加的回调函数,不论什么时候,都会被调用
  • 可以添加多个回调函数,会按照插入顺序并且独立运行
    const p = new Promise(function(resolve,reject){
    resolve(1);
    }).then(function(value){ // 第一个then // 1
    console.log(value);
    return value * 2;
    }).then(function(value){ // 第二个then // 2
    console.log(value);
    }).then(function(value){ // 第三个then // undefined
    console.log(value);
    return Promise.resolve('resolve'); 
    }).then(function(value){ // 第四个then // resolve
    console.log(value);
    return Promise.reject('reject'); 
    }).then(function(value){ // 第五个then //reject:reject
    console.log('resolve:' + value);
    }, function(err) {
    console.log('reject:' + err);
    });
    then 方法将返回一个 resolved 或 rejected 状态的 Promise 对象用于链式调用,且 Promise 对象的值就是这个返回值。

-注意点:

  • 简便的promise链式编程最好保持扁平化,不要嵌套promise

4、理解promise对象

  • Promise 对象具有静态方法和原型方法,
  • Promise 对象中的静态方法可以独立应用,而原型方法需要应用于 Promise对象的实例。
  • 记住,普通方法和原型都返回一个 promise,这使得理解事物变得容易得多。

1、原型方法Prototype Methods

三种方法:
这些方法都可以应用于promise对象的一个实例,并且所有这些方法依次返回一个promise。
当创建一个 Promise 时,它处于 pending 状态。Promise 根据是否 fulfilled(已成功)或rejected(已失败),将运行以下三种方法中的一种或多种。

Promise.prototype.catch(onRejected)

Promise.prototype.then(onFulfilled, onRejected)

Promise.prototype.finally(onFinally)

下图显示了 then 和 .catch 方法的流程。

  • 由于它们返回一个 Promise ,它们可以再次被链式调用。
  • 不管 promise 最后的状态,在执行完then 或 catch 指定的回调函数以后,都会执行finally方法指定的回调函数。

图片说明

promise.all方法,Promise.race方法

  • Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
    var p = Promise.all([p1,p2,p3]);

上面代码中,Promise.all 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 对象的实例。(Promise.all 方法的参数不一定是数组,但是必须具有 iterator 接口,且返回的每个成员都是 Promise 实例。)

Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
var p = Promise.all([p1,p2,p3]);
上面代码中,Promise.all 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 对象的实例。(Promise.all 方法的参数不一定是数组,但是必须具有 iterator 接口,且返回的每个成员都是 Promise 实例。)

Promise.race 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
var p = Promise.race([p1,p2,p3]);
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值。
如果Promise.all方法和Promise.race方法的参数,不是Promise实例,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理。

Promise.resolve 方法,Promise.reject 方法

有时需要将现有对象转为Promise对象,Promise.resolve方法就起到这个作用。
var jsPromise = Promise.resolve($.ajax('/whatever.json'));
上面代码将 jQuery 生成 deferred 对象,转为一个新的 ES6 的 Promise 对象。
如果 Promise.resolve 方法的参数,不是具有 then 方法的对象(又称 thenable 对象),则返回一个新的 Promise 对象,且它的状态为fulfilled。
var p = Promise.resolve('Hello');

p.then(function (s){
console.log(s)
});
// Hello
上面代码生成一个新的Promise对象的实例p,它的状态为fulfilled,所以回调函数会立即执行,Promise.resolve方法的参数就是回调函数的参数。
如果Promise.resolve方法的参数是一个Promise对象的实例,则会被原封不动地返回。
Promise.reject(reason)方法也会返回一个新的Promise实例,该实例的状态为rejected。Promise.reject方法的参数reason,会被传递给实例的回调函数。
var p = Promise.reject('出错了');

p.then(null, function (s){
console.log(s)
});
// 出错了
上面代码生成一个Promise对象的实例p,状态为rejected,回调函数会立即执行。

使用 promise 的经验法则

  1. 使用异步或阻塞代码时,请使用 promise。
  2. 为了代码的可读性,resolve 方法对待 then, reject 对应 catch 。
  3. 确保同时写入.catch 和 .then 方法来实现所有的 promise。
  4. 如果在这两种情况下都需要做一些事情,请使用 .finally。
  5. 我们只有一次改变每个promise (单一原则)。
  6. 我们可以在一个promise 中添加多个处理程序。
  7. Promise对象中所有方法的返回类型,无论是静态方法还是原型方法,都是Promise。
  8. 在Promise.all中,无论哪个promise 首先未完成,promise 的顺序都保持在值变量中。