做者: HerryLojavascript
本文永久有效连接: https://github.com/AttemptWeb......java
Promises
对象被用于表示一个异步操做的最终完成 (或失败), 及其结果值。主要是为了解决异步操做的问题。git
一个 Promise对象有如下三种状态:es6
pending: 初始状态,既不是成功,也不是失败状态。 fulfilled(resolved): 意味着操做成功完成。 rejected: 意味着操做失败。
Promise对象内部运行的一个变化, 变化以下:github
1. 当new Promise()被实例化后,即表示Promise 进入pending初始化状态,准备就绪,等待运行。 2. 一旦Promise实例运行成功或者失败以后,实例状态就会变为fulfilled 或者 rejected,此时状态就没法变动。
任何系统或函数均可以简化为输入输出系统,数据输入 ——> 黑箱 ——> 输出
,以下图:npm
咱们能够拿上图来类比Promise函数
,代码以下:c#
// 实例化 Promise new Promise((resolve, reject)=> { // 输入 AjaxRequest.post({ url: 'url', data: {}, sueccess: ()=> { // resolve resolve(res) }, fail: (err)=> { // reject reject(err) } }) }).then((res)=> { // res 输出 // ...操做 }).catch((err)=> { // err 输出 // ...操做 })
在上面的代码中,Promise函数参数能够做为
输入信息,然后通过Promise的内部处理(黑箱
),在then函数或者catch函数参数中
输出信息,这是一个完整的系统(别被它分散了注意力,这个解释的目的:让你更加关注Promise函数内部实现)。下面咱们将解析Promise中黑箱操做。数组
Promise函数实例化,会先进入到pending状态,在这个状态下,它会运行以下函数:promise
你能够直接查看源码:Promise函数:54行,对照阅读,同时,在下面的代码中我会作没必要要的省略。微信
// 首先运行,Promise构造函数 function Promise(fn) { // ...省略检验 // _deferreds的类型,1是 single,2是 array this._deferredState = 0; // 0 - pending // 1 - fulfilled(resolved) // 2 - rejected // 3 - 另外一个Promise的状态 this._state = 0; // promise 执行结果 this._value = null; // then注册回调数组 this._deferreds = null; // fn等于noop 即return if (fn === noop) return; // 接受Promise回调函数 和 this 做为参数 doResolve(fn, this); }
Promise
构造函数,会初始化属性,其中参数fn
就是咱们传入的函数。其中doResolve
函数接受Promise函数参数
和 this
做为参数,this指向它本身,负责执行fn函数。等下面的then
函数和catch
函数的回调函数注册完以后,doResolve
函数将当即执行。
能够查看代码,查看源码:then函数:72行。then方法的回调函数会被存储在this._deferreds
中。仔细阅读代码中的备注
Promise.prototype.then = function(onFulfilled, onRejected) { if (this.constructor !== Promise) { // safeThen函数也是经过调用handle函数,return 新的Promise对象 return safeThen(this, onFulfilled, onRejected); } // 生成新的Promise对象 var res = new Promise(noop); handle(this, new Handler(onFulfilled, onRejected, res)); return res; }; // Handler构造函数 // 它的做用是挂载 then中的回调函数 和 一个空的Promise对象 function Handler(onFulfilled, onRejected, promise){ // then中的Fulfilled回调函数 this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; // then中的Rejected回调函数 this.onRejected = typeof onRejected === 'function' ? onRejected : null; // 保存新的Promise this.promise = promise; }
// 保存then注册回调函数,更新回调函数状态 function handle(self, deferred) { // 。。。省略 // pedding 状态 if (self._state === 0) { // deferred == new Handler(onFulfilled, onRejected, res) if (self._deferredState === 0) { self._deferredState = 1; // 存储then回调deferred对象 self._deferreds = deferred; return; } if (self._deferredState === 1) { self._deferredState = 2; // 存储then回调deferred对象 self._deferreds = [self._deferreds, deferred]; return; } // 存储then回调函数对象 self._deferreds.push(deferred); return; } // 只有当进入到非pedding状态,handleResolved才会运行 handleResolved(self, deferred); }
Handler
函数生成一个deffer对象,用于保存then函数中的onFulfilled和onRejected回调,以及返回的新的promise实例
then
方法中的核心函数就是handle
函数,它负责接收this
和new Handler
对象。若在pedding
状态下,handle
函数只负责注册回调函数,更新回调函数状态。在非pedding
状态下,则会执行handleResolved
函数。
Promise.prototype['catch'] = function (onRejected) { return this.then(null, onRejected); };
catch
方法的回调函数实际是经过then
方法来完成保存的。
负责运行Promise实例对象中的回调函数参数fn。
// 调用doResolve函数 function doResolve(fn, promise) { var done = false; // tryCallTwo函数执行 相似于 // (resolve, reject) => {if(err){reject(err);return};resolve(res)}执行; var res = tryCallTwo(fn, function (value) { if (done) return; done = true; resolve(promise, value); }, function (reason) { if (done) return; done = true; reject(promise, reason); }); // fn函数调用失败,手动运行reject函数 if (!done && res === IS_ERROR) { done = true; reject(promise, LAST_ERROR); } }
doResolve
是同步直接调用传入的函数。其中tryCallTwo
函数做用是调用函数fn
,它接受三个参数。先执行fn函数,根据结果,再执行resolve
函数或reject
函数。在resolve
函数或reject
函数被调用以前,Promise对象的状态依然是pending
。
pending状态下函数调用基本流程以下:
当初始化完以后,fn函数执行完成,接下来就会运行resolve
函数或者reject
函数。
若Promise对象的fn函数执行正常,以后就会调用resolve函数。能够查看源码:resolve函数:131行。
function resolve(self, newValue) { // 。。。省略 // newValue存在 & (newValue是一个对象 || newValue是一个函数) if ( newValue && (typeof newValue === 'object' || typeof newValue === 'function') ) { // 获取then函数 var then = getThen(newValue); // 。。。省略 if ( then === self.then && newValue instanceof Promise ) { // 若是newValue 是一个Promise对象,那么调用finale函数 self._state = 3; self._value = newValue; finale(self); return; } else if (typeof then === 'function') { // 若是newValue 是一个函数,就继续调用doResolve函数 doResolve(then.bind(newValue), self); return; } } // 标记完成,进入结束流程 self._state = 1; self._value = newValue; finale(self); }
确认newValue
的值,若是newValue是一个函数,就继续循环调用doResolve
函数;若是newValue 是一个Promise对象,那么就直接调用finale
函数。都不是,则直接调用finale
函数。
进入结束流程,finale结束。
function finale(self) { // 单个回调 if (self._deferredState === 1) { // 执行handle函数,实际是执行handleResolved handle(self, self._deferreds); self._deferreds = null; } // 回调数组 if (self._deferredState === 2) { for (var i = 0; i < self._deferreds.length; i++) { // 执行handle函数,实际是执行handleResolved handle(self, self._deferreds[i]); } self._deferreds = null; } }
finale
函数表示进入结束流程,执行handle
函数。同时在上面已经说到,在非pedding
状态下,执行handle
函数,实际会是执行handleResolved
函数。
handleResolved
负责收尾工做,负责执行then或者catch方法注册的回调函数。仔细阅读代码中的备注
var asap = require('asap/raw'); function handleResolved(self, deferred) { asap(function() { var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; // 不存在 onFulfilled & onRejected // deferred.promise 只是一个空的Promise对象 if (cb === null) { // 1 - fulfilled(resolved) if (self._state === 1) { resolve(deferred.promise, self._value); } else { reject(deferred.promise, self._value); } return; } // 执行cb回调函数 var ret = tryCallOne(cb, self._value); if (ret === IS_ERROR) { // 错误,报reject reject(deferred.promise, LAST_ERROR); } else { resolve(deferred.promise, ret); } }); }
经过异步asap
调用,若不存在onFulfilled
和onRejected
,直接调用resolve
或reject
。若存在,则tryCallOne
回调的结果,直接调用resolve
或reject
。其中的deferred
就是上文提到的new Handler
实例对象。真正会影响最后这步流程的,实际上是deferred.onFulfilled
或者 deferred.onRejected
的回调执行,执行完回调后,这个Promise的执行过程就基本完成。
而reject
函数在这里我就不说了,有兴趣的能够看查看源码:reject函数
Promise对象调用函数的基本流程图,只是一个大体的走向,便于理解:
ps: 微信公众号:Yopai,有兴趣的能够关注,每周不按期更新,分享能够增长世界的快乐