A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© hfy198701 中级黑马   /  2019-8-15 23:39  /  1070 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

promise是什么promise是一个对象
“承诺将来会执行”的对象在JavaScript中称为Promise对象
Promise 是一个关联了执行任务的承诺,当你的任务完成时,会根据任务的成功与否,执行相应的操作。
用法
new Promise((resolve, reject) => setTimeout(resolve, 1000, 'foo')).then()实例
function test(resolve,reject) {        setTimeout(()=>{            if(true){                resolve({data:100})            }else{                reject({data:0})            }        },2000)    }    let p1 = new Promise(test)    p1.then(        res =>{            console.log(res)  //{data:100}        }    )上述实例中test就是要执行的任务,当你的任务完成时会根据成功与否,执行相应的操作
如果成功会通过调用resolve回调,并会把执行的结果作为参数,然后可以再then里面做相应的操作
有没有很熟悉的感觉,axios和ajax都是基于promisede http库,内部都是返回了一个new Promise()对象
在使用 Promise 的时候,我们最简单的理解与用法就是像上面的代码那样,把异步结果提供给 resolve 作参数,然后通过给 then 方法传递一个自定义函数作为结果处理函数。但 resolve 和 reject 这两个参数到底是什么?在这背后,它的基本工作方式到底是怎样的呢?让我们从规范的角度来初步了解它吧。
知识点
  • promise 的工作机制与 callback 类似,都是异步
  • Promise 构造函数里的 resolve/reject 函数是内部创建的,在调用它们时传入的参数就是要解析的结果
  • Promise.prototype.then 根据当前的 promise 的状态来决定是立即将 promise 中存储的结果取出并和参数中的处理函数一起直接插入到 Job 队列中还是先与 promise 关联起来作为结果处理函数。then 会隐式调用 Promise 构建函数构建新的 promise 并返回。
  • Promise.all 先创建一个新的 promise,然后先、初始化一个空的结果数组和一个计数器来对已经 resolve 的 promise进行计数,之后会进行迭代,对于每个迭代值它都会为其创造一个promise,并设定这个promise的then为向结果数组里添加结果以及计数器--,当计数器减至0时就会resolve最终结果。
    Promise.all(test1,test2)两个执行任务都指向成功之后,返回一个数组,数组里面是两个任务的结果
  • Promise.race 也是会创建一个新的主 promise,之后主要是根据 promise 只能 resolve 一次的限制,对于每个迭代值都会创造另一个promise,先resolve的也就会先被主 promise resolve 返回结果。返回晚的自然被废弃不用
疑问点关于promise有问题的点
 Promise 对象有三种状态:pending、fullfilled 和 rejected,分别代表了 promise 对象处于等待、执行成功和执行失败状态。创建 promise 对象后处于pending状态,pending状态可以转化为fullfilled或rejected状态,但不能逆向转化,且转化过程只能有一次,即resolve或reject后不能再resolve或reject。因此需要在 promise 对象中持有状态的引用,通过添加一个名为_state(为了说明是内部属性,用户不要直接使用,属性名前加了下划线,后面同理)的属性实现。现在 Promise 构造函数定义如下:
function Promise(resolver) {    this._status = 'pending';}任务(resolver)内封装了需要执行的异步操作(当然,也可以是同步操作)。同时resolver调用时会被传递两个参数:resolve和reject函数,来自于 Promise 内部的封装,分别代表任务执行成功或者失败时需要执行的操作。任务成功与否由调用者控制,且需要在成功或失败时调用resolve或reject函数,以此来标识当前 promise 对象的完成,并会触发后续 promise 的执行。
 在调用Promise构造函数时,resolver会被立即调用。因此,现在Promise构造函数如下:
function Promise(resolver) {    this._status = 'pending';    resolver(resolve, reject);    ...}Promise 代表着一个承诺。作为承诺,总需要有一个结果,无论成功与否。如果成功,我们会获得需要的结果;当然也有可能会失败。因此我们需要在这个承诺在未来某个时刻有结果时,分别针对结果的成功或失败做相应的处理。因此 Promise 中提供了then方法来完成这个任务。then方法接收两个参数:onResolve和onReject,分别代表当前 promise 对象在成功或失败时,接下来需要做的操作。现实生活中,人们总系喜欢给出各种许诺,同样在代码的世界里,我们也经常会有一连串前后依赖的 promise 需要执行,如下面的调用方式:promise.then().then()...。因此为了方便链式调用,then方法的实现中,都会返回一个新的 promise 对象,就像 jQuery 的方法中一般都会将自己(this)返回一样(不同的是,jQuery中返回的是自身,但在 Promise 中,返回的是一个新的 promise 对象。如果此处也返回自身的话,则串行操作就变成并行操作了,显然不符合我们的目标)。因此,then方法的定义如下:
Promise.prototype.then = function(onResolve, onReject) {    var promise = new Promise(function() {});    ...    return promise;}此处then方法内创建的 promise 对象和暴露给用户直接调用的 Promise 构造函数所创建的 promise 对象有些不同。用户调用 Promise 构造函数时需要传递resolver参数代表与此 promise 对象关联的任务,且任务会立即执行。在未来某个时刻,用户根据任务执行的结果来判断任务是成功还是失败,并且需要调用resolver中被传入的参数resolve或reject来结束此 promise,并由此触发下一个 promise(即当前 promise 对象调用then方法所创建的 promise 对象)所关联的任务的执行。由此可知以下两点:首先then方法中创建的 promise 关联的任务不能在 promise 对象创建时立即执行,所以先传入一个空函数以符合 Promise 构造函数调用格式;其次前一个 promise 对象需要能够知道下一个 promise 对象是谁,其关联的任务是什么,这样才能在自己完成后调用下一个 promise 的任务。因此前一个 promise 需要持有下一个 promise 以及其任务的引用。由于 promise 的执行可能会成功也可能会失败,因此后一个 promise 一般会提供成功或失败后需要执行的任务供前一个 promise 调用。因此前一个 promise 持有下一个 promise 的任务引用时需要区分这一点。promise 的调用不一定都如promise.then().then()...这样的串行方式,也可以有如下的并行方式:
   var promise = new Promise(xxx);    promise.then();    promise.then();此时当前一个 promise 对象完成后,会同时调用两个then方法中创建的 promise 关联的任务。因此,前一个 promise 对象可能需要持有多个 promise 对象以及它们关联的成功和失败任务的引用。因此需要给 promise 对象添加属性用于这些数据的记录
此处我采用了闭包的方式实现:在 promise 对象中增加分别代表成功回调和失败回调的两个数组,数组中的每一项是通过内部封装的闭包函数调用的结果,也是一个函数。只不过这个函数可以访问到内部调用闭包时传递的 promise 对象,因此通过这种方式也可以访问到我们需要的下一个 promise 以及其关联的成功、失败回调的引用。所以现在有两处改动。首先需要在 Promise 构造函数中增加两个属性。现在 Promise 构造函数的定义如下:
function Promise(resolver) {    this._status = 'pending';    this._doneCallbacks = [];    this._failCallbacks = [];    resolver(resolve, reject);    ...}Promise.prototype.then = function(onResolve, onReject) {    var promise = new Promise(function() {});    this._doneCallbacks.push(makeCallback(promise, onResolve, 'resolve'));    this._failCallbacks.push(makeCallback(promise, onReject, 'reject'));    return promise;}then方法中调用的makeCallback即上面说到的闭包函数。调用时会把 promise 对象以及相应的回调传递进去,且会返回一个新的函数,前一个 promise 对象持有返回函数的引用,这样在调用返回函数时,在函数内部就可以访问到 promise 对象以及回调函数了。由于成功回调onResolve和失败回调onReject都通过此闭包封装,所以在闭包中增加了第三个参数action,以区分是哪种回调。现在makeCallback的定义如下:
function makeCallback(promise, callback, action) {    return function promiseCallback(value) {        ...    };}前面说过,调用构造函数创建 promise 对象时需要传递作为任务的函数resolver,resolver会被立即调用,并被传递参数resolve和reject函数,用于结束当前 promise 并触发接下来的 promise 的调用。下面将介绍resolve和reject函数的实现。
我们使用 promise,是期望在未来的某个时刻能获得一个结果,并且可用于接下来的 promise 调用。所以resolve函数需要有一个参数来接收结果(同样,promise 执行失败后,我们也希望在后续 promise 中获得此失败信息,做相应处理。所以reject函数也需要有一个参数来接收错误)。前面说过 promise 对象的状态只能由pending状态转换为fullfilled或rejected状态,且只能转换一次。所以resolve或reject时,需要判断一下状态。所以,现在resolve和reject函数的定义如下:
    function resolve(promise, data) {        if (promise._status !== 'pending') {            return;        }        promise._status = 'fullfilled';        promise._value = data;        run(promise);    }    function reject(promise, reason) {        if (promise._status !== 'pending') {            return;        }        promise._status = 'rejected';        promise._value = reason;        run(promise);    }resolve和reject函数也可以定义在 Promise 构造函数的 prototype 上,这样可直接通过promise.resolve(data)或promise.reject(reason)调用,不用传递第一个参数promise。但由于此函数是内部调用,为了不暴露不必要的接口给用户,所以定义为内部函数。由于执行时需要知道是 resolve 或 reject 哪一个 promise 对象,所以需要多一个名为promise参数。resolve和reject函数中首先判断了当前 promise 的状态,如果不是pending(即已经被 resolve 或 reject 过了,不再重复执行),则直接返回。然后赋予 promise 新的状态,并保存成功或失败的值。最后调用run函数。run函数用于触发接下来的 promise 的执行。run函数中需要注意的一点是,需要异步执行相关的回调函数。run函数的定义如下:
function run(promise) {        // `then`方法中也会调用,所以此处仍需做一次判断        if (promise._status === 'pending') {            return;        }        var value = promise._value;        var callbacks = promise._status === 'fullfilled'            ? promise._doneCallbacks            : promise._failCallbacks;        // Promise需要异步操作        setTimeout(function () {            for (var i = 0, len = callbacks.length; i < len; i++) {                callbacks(value);            }        });        // 每个promise只能被执行一次。虽然`_doneCallbacks`和`_failCallbacks`用户不应该直接访问,        // 但还是可以访问到,保险起见,做清空处理。        promise._doneCallbacks = [];        promise._failCallbacks = [];    }run函数中调用的 callback,就是前面闭包函数makeCallback返回的函数。makeCallback函数是整个代码中较复杂的部分。其实现过程基本是按照 Promises/A+规范(中文)(英文参见 Promises/A+规范(英文))中的[Promise 解决过程]部分来完成的。可参照规范部分,此处就不具体介绍了。
    function makeCallback(promise, callback, action) {        return function promiseCallback(value) {            // 如果传递了callback,则使用前一个promise传递过来的值作为参数调用callback,            // 并根据callback的调用结果来处理当前promise            if (typeof callback === 'function') {                var x;                try {                    x = callback(value);                }                catch (e) {                    // 如果调用callback时抛出异常,则直接用此异常对象reject当前promise                    reject(promise, e);                }                // 如果callback的返回值是当前promise,为避免造成死循环,需要抛出异常                // 根据Promise+规范,此处应抛出TypeError异常                if (x === promise) {                    var reason = new TypeError('TypeError: The return value could not be same with the promise');                    reject(promise, reason);                }                // 如果返回值是一个Promise对象,则当返回的Promise对象被resolve/reject后,再resolve/reject当前Promise                else if (x instanceof Promise) {                    x.then(                        function (data) {                            resolve(promise, data);                        },                        function (reason) {                            reject(promise, reason);                        }                    );                }                else {                    var then;                    (function resolveThenable(x) {                        // 如果返回的是一个Thenable对象(此处逻辑有点坑,参照Promise+的规范实现)                        if (x && (typeof x === 'object'|| typeof x === 'function')) {                            try {                                then = x.then;                            }                            catch (e) {                                reject(promise, e);                                return;                            }                            if (typeof then === 'function') {                                // 调用Thenable对象的`then`方法时,传递进去的`resolvePromise`和`rejectPromise`方法(及下面的两个匿名方法)                                // 可能会被重复调用。但Promise+规范规定这两个方法有且只能有其中的一个被调用一次,多次调用将被忽略。                                // 此处通过`invoked`来处理重复调用                                var invoked = false;                                try {                                    then.call(                                        x,                                        function (y) {                                            if (invoked) {                                                return;                                            }                                            invoked = true;                                            // 避免死循环                                            if (y === x) {                                                throw new TypeError('TypeError: The return value could not be same with the previous thenable object');                                            }                                            // y仍有可能是thenable对象,递归调用                                            resolveThenable(y);                                        },                                        function (e) {                                            if (invoked) {                                                return;                                            }                                            invoked = true;                                            reject(promise, e);                                        }                                    );                                }                                catch (e) {                                    // 如果`resolvePromise`和`rejectPromise`方法被调用后,再抛出异常,则忽略异常                                    // 否则用异常对象reject此Promise对象                                    if (!invoked) {                                        reject(promise, e);                                    }                                }                            }                            else {                                resolve(promise, x);                            }                        }                        else {                            resolve(promise, x);                        }                    }(x));                }            }            // 如果未传递callback,直接用前一个promise传递过来的值resolve/reject当前Promise对象            else {                action === 'resolve'                    ? resolve(promise, value)                    : reject(promise, value);            }        };    }


0 个回复

您需要登录后才可以回帖 登录 | 加入黑马