第 1 章 Promise的理解和使用

267 阅读11分钟

1.1. Promise 是什么?

1.1.1. 理解

  • 抽象表达: Promise 是 JS 中进行异步编程的新的解决方案(旧的是谁?)
  • 具体表达:
    • 从语法上来说: Promise 是一个构造函数
    • 从功能上来说: promise 对象用来封装一个异步操作并可以获取其结果

1.1.2 promise 的状态

  • 待定(pending) :初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled) :意味着操作成功完成。
  • 已拒绝(rejected) :意味着操作失败。

1.1.3 状态的改变

  1. pending 变为 resolved
  2. pending 变为 rejected
说明: 
    只有这 2 种状态变化, 且一个 promise 对象只能改变一次
    无论变为成功还是失败, 都会有一个结果数据
    成功的结果数据一般称为 vlaue, 失败的结果数据一般称为 reason

1.1.4 promise 的基本流程

image.png

1.1.5 promise 的基本使用

我们使用new Promise构造器创建一个promise实例对象,并且必须要传入executor函数作为参数。

let p = new Promise((resolve, reject) => {});
setTimeout(console.log, 0, p); // Promise <pending>

执行器函数主要有两项职责:初始化期约的异步行为和控制状态的最终转换。在执行器函数中调用 resolve()会把状态切换为兑现,调用 reject()会把状态切换为拒绝,拒绝后需要捕获错误。

let p1 = new Promise((resolve, reject) => resolve());
setTimeout(console.log, 0, p1); // Promise <resolved>
let p2 = new Promise((resolve, reject) => reject());
setTimeout(console.log, 0, p2); // Promise <rejected>
// Uncaught error (in promise)

promise实例通过then方法处理异步程序。then()方法接受最多两个参数:onResolved 处理程序和 onRejected 处理程序。这两个参数都是可选的,如果提供的话,则会在期约分别进入“兑现”和“拒绝”状态时执行。

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    const time = Date.now() // 如果当前时间是偶数就代表成功, 否则代表失败
    //  如果成功了, 调用resolve(value)
    if (time % 2 == 0) {
      resolve('成功的数据, time=' + time)
    } else {
    //  如果失败了, 调用reject(reason)
      reject('失败的数据, time=' + time)
    }
  }, 1000)
})

p.then(
    value => { // 接收得到成功的value数据    onResolved
        console.log('成功的回调', value)  
    },
    reason => {// 接收得到失败的reason数据  onRejected
        console.log('失败的回调', reason)
    }
)

1.2 为什么要使用promise

1.2.1 早期的异步编程

在早期的 JavaScript 中,只支持定义回调函数来表明异步操作完成。串联多个异步操作是一个常见的问题,通常需要深度嵌套的回调函数(俗称“回调地狱”)来解决。

function double(value, success, failure) {
    setTimeout(() => {
        try {
            if (typeof value !== 'number') {
                throw 'Must provide number as first argument';
            }
            success(2 * value);
        } catch (e) {
            failure(e);
        }
    }, 1000);
}
const successCallback = (x) => console.log(`Success: ${x}`);
const failureCallback = (e) => console.log(`Failure: ${e}`);
double(3, successCallback, failureCallback);
double('b', successCallback, failureCallback);

这种方式必须在初始化异步操作时定义回调。如果异步返值又依赖另一个异步返回值,那么回调的情况还会进一步变复杂。

1.2.2 promise处理结果回调

promise可以在异步执行之后指定回调函数,并且支持链式调用。

let p = new Promise((resolve, reject) => {
    resolve();
});

p.then(() => console.log('second'))
.then(() => console.log('third'))
.then(() => console.log('fourth'));

// first
// second
// third
// fourth

1.2.3 async/await 终极解决异步方案

虽然promise解决了回调地狱的问题,但本质上还是使用了回调函数的方式。异步函数,也称为“async/await”(语法关键字),是 ES6 期约模式在 ECMAScript 函数中的应用。async/await 是 ES8 规范新增的。这个特性从行为和语法上都增强了 JavaScript,让以同步方式写的代码能够异步执行。

async function request() {
    try {
      const result = await doSomething()
      const newResult = await doSomethingElse(result)
      const finalResult = await doThirdThing(newResult)
    } catch (error) {
      failureCallback(error)
    }
}

以上伪代码表示,依次执行 doSomething,doSomethingElse,doThirdThing,并得到最终结果finalResult。 如果发生错误,会在failureCallback中捕获处理。

1.3 Promise的使用

1.3.1 Promise 构造函数

Promise(excutor)

const excutor = (resolve, reject) => {}
  • excutor 函数:执行器 (resolve, reject) => {}
  • resolve 函数: 内部定义成功时我们调用的函数 value => {}
  • reject 函数: 内部定义失败时我们调用的函数 reason => {}
  • excutor 会在 Promise 内部立即同步回调,异步操作在执行器中执行

1.3.2 Promise.prototype.then

promise.then(onResolved, onRejected)
  • onResolved 函数: 成功的回调函数 (value) => {}
  • onRejected 函数: 失败的回调函数 (reason) => {}
  • 指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调
  • then 方法返回一个新的promise实例对象

then方法指定回调函数是同步的,但是then方法中指定的onResolved 函数和onRejected 函数是异步执行。

new Promise((resolve, reject) => {
  resolve(1) // 先改变的状态(同时指定数据)
}).then(// 后指定回调函数, 异步执行回调函数
  value => {console.log('value', value)},
  reason => {console.log('reason', reason)}
)
console.log('先输出')

1.3.3 Promise.prototype.catch

promise.catch(onRejected)
  • onRejected 函数: 失败的回调函数 (reason) => {}
  • catch方法相当于then方法的语法糖:then(undefined,onRejected)

Promise.resolve

静态方法。当传入的参数非promise对象时,会返回一个包装当前值的resolved状态的promise对象;当传入的参数是一个promise对象是,返回的是跟当前保持一致状态的promise对象。

let p1 = Promise.resolve(1)
p1.then(value => console.log(value)) // 返回的是resolved状态的promise对象,可以在onResolved回调中接受,此时value 为1

let p2 = new Promise((resovle, reject) => reject('err'))
let p3 = Promise.resolve(p2)
p3.catch(reason => console.log(reason)) // err

let p4 = new Promise((resovle, reject) => resolve('sucess'))
let p5 = Promise.resolve(p4)
p4.then(value => console.log(value)) // sucess

Promise.reject

静态方法。返回一个rejected状态的promise对象。

// 传入的是一个非promise值
let p1 = Promise.reject(1)
p1.catch(value => console.log('p1',value)) // 返回的是rejected状态的promise对象,可以在 onRejected回调/catch方法 中接受,此时value 为1

// 传入一个 rejected 状态的 promise对象
let p2 = new Promise((resolve, reject) => {reject('p2-err')})
p2.catch(reason => console.log('p2', reason)) // p2-err
let p3 = Promise.reject(p2)
// 此时的reason就是p2,reason 通用可以在 onRejected回调/catch方法 捕获错误
p3.catch(reason => console.log('p3',reason)) 

// 传入一个 resolved 状态的 promise对象
let p4 = new Promise((resolve, reject) => resolve('sucess'))
let p5 = Promise.reject(p4)
// 此时的reason就是p4,reason 通用可以在 onResolved回调中接受值
p5.catch(reason => console.log('p5',reason.then(val => console.log('val',val)))) // sucess

Promise.all

静态方法。接受一个包含多个promise对象的数组,返回一个新的promise;只有所有的promise都成功才成功, 只要有一个失败了就直接失败;成功的时候,在onResolved回调中接受的values是一个数组,且数组中按传入的promise顺序一一对应;失败的时候,原因是第一个失败的原因。

const p1 = new Promise((resolve, reject) => {
   setTimeout(() => {
    resolve(1)
   }, 100);
})
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)
const p4 = new Promise((resolve, reject) => {
    setTimeout(() => {
       reject(4)
    },100)
})

当成功时,返回的values数组中的value跟传入的promise对象顺序一一对应

// p2的状态改变要比p1的状态改变早
const pAll = Promise.all([p1, p2])
pAll.then(values => console.log(values)) // [1,2]

当失败时,失败的原因时第一个失败的原因

const pAll = Promise.all([p1, p2, p4, p3])
pAll.catch(reason => console.log(reason)) // 3,因为p3是最早失败的

Promise.race

静态方法。接受一个包含多个promise对象的数组,返回的结果是由首个状态改变的promsie对象决定。

当首个promsie对象的状态改变是成功时,返回的是当前promise对象成功的value

const pRace = Promise.race([p1, p2, p3])
pRace.then(
 value => {
    console.log(value)
 },
 reason => {
    console.log(reason)
 }
)
// 最终输出2

当首个promise对象的状态改变是失败时,返回的是当前promise对象失败的reason

const pRace = Promise.race([p3, p2, p1])
pRace.then(
 value => {
    console.log(value)
 },
 reason => {
    console.log(reason)
 }
)
// 最终输出3

1.4 promise的几个关键问题

1.4.1 如何改变promise的状态?

resolve(value): 如果当前是pendding就会变为resolved

const p = new Promise((resolve, reject) => resolve('sucess'))
p.then(value => console.log(value)) // 输出 sucess
   

reject(reason): 如果当前是pendding就会变为rejected

const p = new Promise((resolve, reject) => reject('reason'))
p.catch(reason => console.log(reason)) // 输出 reason

抛出异常: 如果当前是pendding就会变为rejected

const p1 = new Promise((resolve, reject) => throw Error('reason'))
p1.catch(reason => console.log(reason)) // 输出 reason
// 可以throw 任意值,不一定是Error
const p2 = new Promise((resolve, reject) => throw ('reason'))
p2.catch(reason => console.log(reason)) // 输出 reason

1.4.2 一个promise指定多个成功/失败回调函数, 都会调用吗?

当指定多个成功的回调函数

const p = new Promise((resolve, reject) => resolve('sucess'))
p.then(value => console.log('val1:',value)) // 输出 val1:sucess
p.then(value => console.log('val2:',value)) // 输出 val2:sucess

当指定多个失败的回调函数

const p = new Promise((resolve, reject) => reject('reason'))
p.catch(reason => console.log('reason1:',reason)) // 输出 reason1:reason
p.catch(reason => console.log('reason2:',reason)) // 输出 reason2:reason

因此,当promise改变为对应状态时,指定的对应回调函数都会调用

1.4.3 改变promise状态和指定回调函数谁先谁后?

一般情况: 先指定回调函数, 后改变的状态

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1) // 后改变的状态(同时指定数据), 异步执行回调函数
  }, 1000);
}).then(// 先指定回调函数, 保存当前指定的回调函数
  value => {},
  reason => {console.log('reason', reason)}
)

我们也可以先改变状态,后指定回调函数

new Promise((resolve, reject) => {
  resolve(1) // 先改变的状态(同时指定数据)
}).then(// 后指定回调函数, 异步执行回调函数
  value => {console.log('value', value)},
  reason => {console.log('reason', reason)}
)

1.4.4 promise.then()返回的新 promise 的结果状态由什么决定?

用一句话来概括:由then()指定的回调函数执行的结果决定

当then中执行的onResolved/onRejected 返回一个非promise的任意值,新promise变为resolved, value为返回的值

当then中执行onResolved回调,并且返回一个非promise的任意值

new Promise((resolve, reject) => {
  resolve(1)
}).then(
  value => {
    return 2
  },
  reason => {
    return 2
  }
).then(
  value => {
    console.log('onResolved()', value) // 新的promise是resolved状态,输出:onResolved() 2
  },
  reason => {
    console.log('onRejected()', reason)
  }
)

当then中执行onRejected回调,并且返回一个非promise的任意值

new Promise((resolve, reject) => {
  reject(1)
}).then(
  value => {
    return 2
  },
  reason => {
    return 2
  }
).then(
  value => {
    console.log('onResolved()', value) //新的promise是resolved状态, 输出:onResolved() 2
  },
  reason => {
    console.log('onRejected()', reason)
  }
)

当then中执行的onResolved/onRejected 返回的是另一个新promise, 此promise的结果就会成为新promise的结果

当then中执行onResolved回调,并且返回一个promise

new Promise((resolve, reject) => {
  resolve(1)
}).then(
  value => {
    return Promise.resolve(3)
  }
).then(
  value => {
    console.log('onResolved()', value) // 新的promise是成功状态,输出onResolved() 3
  },
  reason => {
    console.log('onRejected()', reason)
  }
)
new Promise((resolve, reject) => {
  resolve(1)
}).then(
  value => {
    return Promise.reject(3)
  }
).then(
  value => {
    console.log('onResolved()', value) 
  },
  reason => {
    console.log('onRejected()', reason)// 新的promise是失败状态,onRejected() 3
  }
)

当then中执行onRejected回调,并且返回一个promise

new Promise((resolve, reject) => {
  reject(1)
}).then(
 undefined,
  reason => {
      return Promise.resolve(3)
  }
).then(
  value => {
    console.log('onResolved()', value) // 新的promise是成功状态,输出onResolved() 3
  },
  reason => {
    console.log('onRejected()', reason)
  }
)
new Promise((resolve, reject) => {
  reject(1)
}).then(
 undefined,
  reason => {
      return Promise.reject(3)
  }
).then(
  value => {
    console.log('onResolved()', value) 
  },
  reason => {
    console.log('onRejected()', reason)// 新的promise是失败状态,输出onRejected() 3
  }
)

当then中执行的onResolved/onRejected中抛出异常, 新promise变为rejected, reason为抛出的异常

new Promise((resolve, reject) => {
  resolve(1)
}).then(
  value => {
    throw 3
  }
).then(
  value => {
    console.log('onResolved()', value) 
  },
  reason => {
    console.log('onRejected()', reason)// 新的promise是失败状态,输出onRejected() 3
  }
)
new Promise((resolve, reject) => {
  reject(1)
}).then(
  undefined,
  reason => {
      throw 3
  }
).then(
  value => {
    console.log('onResolved()', value) 
  },
  reason => {
    console.log('onRejected()', reason)// 新的promise是失败状态,输出onRejected() 3
  }
)

1.4.5 promise 如何串联多个操作任务

promise的then()返回一个新的promise, 可以开成then()的链式调用,通过then的链式调用串连多个同步/异步任务

new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log("执行任务1(异步)")
    resolve(1)
  }, 1000);
}).then(
  value => {
    console.log('任务1的结果: ', value) // 输出:1
    console.log('执行任务2(同步)')
    return 2
  }
).then(
  value => {
    console.log('任务2的结果:', value) // 输出: 2
    return new Promise((resolve, reject) => {
      // 启动任务3(异步)
      setTimeout(() => {
        console.log('执行任务3(异步))')
        Promise.resolve(3)
      }, 1000);
    })
  }
).then(
  value => {
    console.log('任务3的结果: ', value) // 输出:3
  }
)

如果then中回调函数返回的是一个异步任务,需要包装成一个新的promise返回。

1.4.6 promise 异常穿透

当使用promise的then链式调用时,可以在最后指定失败的回调

new Promise((resolve, reject) => {
  reject(1)
}).then(
  value => {
    console.log('onResolved1()', value)
    return 2
  }
).then(
  value => {
    console.log('onResolved2()', value)
    return 3
  }
).then(
  value => {
    console.log('onResolved3()', value)
  }
).catch(reason => {
  console.log('onRejected1()', reason) // 最终输出 onRejected1() 1
})

上述代码中,在前面then的链式调用中没有指定失败的回调,所以在最后catch中捕获错误。前面没有指定失败的回调,相当于在失败的回调返回一个失败的promise并且理由为,首次失败时的reason。

new Promise((resolve, reject) => {
  reject(1)
}).then(
  value => {
    console.log('onResolved1()', value)
    return 2
  },
  reason => {throw reason} // 向后抛出reason
).then(
  value => {
    console.log('onResolved2()', value)
    return 3
  },
  reason => {throw reason} // 向后抛出reason
).then(
  value => {
    console.log('onResolved3()', value)
  },
  reason => Promise.reject(reason) // 向后返回一个状态为rejected的promise
).catch(reason => {
  console.log('onRejected1()', reason) // 最终输出 onRejected1() 1
})

1.4.6 中断promise链

上述代码中,catch捕获后依然返回了一个成功的promise,并且value为undefined

new Promise((resolve, reject) => {
  reject(1)
}).then(
  value => {
    console.log('onResolved1()', value)
    return 2
  },
  reason => {throw reason} // 向后抛出reason
).then(
  value => {
    console.log('onResolved2()', value)
    return 3
  },
  reason => {throw reason} // 向后抛出reason
).then(
  value => {
    console.log('onResolved3()', value)
  },
  reason => Promise.reject(reason) // 向后返回一个状态为rejected的promise
).catch(reason => {
  console.log('onRejected1()', reason)
}).then(
   value => console.log(value)  // 输出:undefined
)

如何中断后续的then方法中的回调执行呢?换一句话说,then方法中的回调在什么时候执行?

then方法中指定的onResolve和onReject 回调函数分别是在promise状态resolved和rejected时执行,那么也就是说当promise的状态为pendding时,两个回调是不执行的。因此,我们可以通过返回一个pending状态的promise来中断promise的链式调用

new Promise((resolve, reject) => {
  reject(1)
}).then(
  value => {
    console.log('onResolved1()', value)
    return 2
  },
  reason => {throw reason} // 向后抛出reason
).then(
  value => {
    console.log('onResolved2()', value)
    return 3
  },
  reason => {throw reason} // 向后抛出reason
).then(
  value => {
    console.log('onResolved3()', value)
  },
  reason => Promise.reject(reason) // 向后返回一个状态为rejected的promise
).catch(reason => {
  console.log('onRejected1()', reason)
  return new Promise((resolve,reject)=>{})
}).then(
   value => console.log(value) ,  // 没有执行
   reason => console.log(reason) // 没有执行
)