资讯专栏INFORMATION COLUMN

Promise——从阅读文档到简单实现(一)

yanwei / 1727人阅读

摘要:意味着操作成功完成。方法接收失败情况的回调函数作为参数,返回一个对象。参数回调函数不接收任何参数,当对象变成状态时被调用。现在各个方法的参数返回值功能和使用方法已经有个大概的了解了,为了进一步理解其原理,接下来我打算简单地实现一下它。

前言

最近几周参加笔试面试,总是会遇到实现异步和处理异步的问题,然而作者每次都无法完美地回答。在最近一次笔试因为 Promise 而被刷掉后,我终于下定决心一个个地搞懂它们,就先拿 Promise 开刀吧 :)。

用法解析

ES6 的Promise对象是一个代理对象,被代理的值在Promise对象创建时可能是未知的,另外它允许你为异步操作的成功和失败分别绑定相应的处理方法。
Promise 常用于控制异步操作的执行顺序,而且可以让异步方法像同步方法那样返回值。它不能立即取得异步方法的返回值,但是它可以代理这个值,一旦异步操作完成,就会以及将值传递给相应的处理方法。
一个Promise对象有以下几种状态:

pending: 初始状态,既不是成功,也不是失败状态。

fulfilled: 意味着操作成功完成。

rejected: 意味着操作失败。

一个Promise对象的状态可以从pending变成fulfilled,同时传递一个值给相应的onfulfilled处理方法;也可以从pending变成rejected,同时传递一个失败信息给相应的onrejected处理方法。
一旦一个Promise对象的状态发生改变,就会触发之前通过Promise.prototype.then、 Promise.prototype.catch和 Promise.prototype.finally方法绑定的onfulfilledonrejectedonFinally处理方法。
因为 thencatchfinally方法都会返回一个新的Promise对象, 所以它们可以被链式调用。

构造函数

构造函数Promise()主要用来包装还未支持 promises 的函数。

new Promise( function(resolve, reject) {...} /* executor */  );

参数:executor
executor是带有 resolvereject 两个函数参数的函数。Promise构造函数执行时立即调用executor函数,换句话说,executor函数是在Promise构造函数内执行的,所以它是同步代码。在executor函数内调用 resolvereject 函数,可以传递参数给相应的处理方法,并会分别将 promise 新建对象的状态改为fulfilled(完成)或rejected(失败)。
executor 内部通常会执行一些异步操作如ajax,一旦完成,可以调用resolve函数传递参数并将 promise 对象的状态改成 fulfilled,或者在发生错误时调用reject 函数传递参数并将 promise 对象的状态改成 rejected。如下:

function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onload = () => resolve(xhr.responseText);
    xhr.onerror = () => reject(xhr.statusText);
    xhr.send();
  });
};

如果在executor函数中抛出一个错误,那么该 promise 对象状态将变为rejected,并将错误作为参数传递给对应的onrejected处理方法。如下:

function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onerror = () => {
        throw xhr.statusText;
        //throw xhr.statusText 效果等同于 reject(xhr.statusText)
    };
    xhr.send();
  });
};
静态方法

静态方法是定义在构造函数上的方法,声明静态方法:

Func.fn = function() {}

调用静态方法:

Func.fn(args);

ES6中的Promise构造函数有4个静态方法:

Promise.resolve(value)

Promise.reject(reason)

Promise.all(iterable)

Promise.race(iterable)

Promise.resolve(value):
返回一个由参数value解析而来的Promise对象。

如果传入的value本身就是Promise对象,则直接返回value

如果value是一个thenable对象(带有 then 方法的对象),返回的Promise对象会跟随这个thenable对象,状态随之变化;

其它情况下返回的Promise对象状态为fulfilled,并且将该value作为参数传递给onfulfilled处理方法。

通常而言,如果你不知道一个值是否为Promise对象,就可以使用 Promise.resolve(value) 来将valuePromise对象的形式使用。

    // resolve一个thenable对象
    var p1 = Promise.resolve({ 
      then: function(onFulfill, onReject) { onFulfill("fulfilled!"); }
    });
    console.log(p1 instanceof Promise) // true, 这是一个Promise对象
    
    p1.then(function(v) {
        console.log(v); // 输出"fulfilled!"
      }, function(e) {
        // 不会被调用
    });

Promise.reject(reason):
返回一个被给定原因reason拒绝的Promise对象。
返回的Promise对象的status状态属性为rejectedreason拒绝原因属性(传递给onrejected处理方法的 reason 参数)与参数reason相等。

Promise.reject(new Promise((resolve, reject) => resolve("done")))
.then(function(reason) {
      // 未被调用
}, function(reason) {
      console.log(reason); // new Promise
});

Promise.all(iterable):
参数:iterable对象为Array对象、Map对象和Set对象等可迭代对象。
返回:一个Promise对象。

如果iterable不是一个可迭代对象,Promise.all会同步地返回返回一个状态为rejected ,拒绝原因reson为类型错误的 promise 对象;

如果iterable对象为空,Promise.all会同步地返回一个状态为fulfilledvalue值为空数组的 promise 对象;

如果iterable对象中的 promise 对象都变为fulfilled状态,或者iterable对象内没有 promise 对象,Promise.all返回的 promise 对象将异步地变为fulfilled状态。这两种情况返回的 promise 对象的value值(传递给onfulfilled处理方法的 value 参数)都是一个数组,这个数组包含iterable对象中所有的基本值和 promise 对象value值,这些值将会按照参数iterable内的顺序排列,而不是由 promise 的完成顺序决定;

如果iterable对象中任意一个 promise 对象状态变为rejected,那么Promise.all就会异步地返回一个状态为rejected的 promise 对象,而且此 promise 对象的reason值(传递给onrejected处理方法的 reason 参数),等于iterable对象中状态为rejected的那一个 promise 对象的reason值。

    // this will be counted as if the iterable passed is empty, so it gets fulfilled
    var p = Promise.all([1,2,3]);
    // this will be counted as if the iterable passed contains only the resolved promise with value "333", so it gets fulfilled
    var p2 = Promise.all([1,2,3, Promise.resolve(333)]);
    // this will be counted as if the iterable passed contains the rejected promise with value "444", so it gets rejected
    var p3 = Promise.all([1,2, Promise.reject(444), Promise.reject(555)]);
    
    // using setTimeout we can execute code after the stack is empty
    setTimeout(function(){
        console.log(p);
        console.log(p2);
        console.log(p3);
    });
    
    // logs
    // Promise { : "fulfilled", : Array[3] }
    // Promise { : "fulfilled", : Array[4] }
    // Promise { : "rejected", : 444 }

Promise.race(iterable):
返回一个Promise对象。

如果iterable不是一个可迭代对象,Promise.race会同步地返回返回一个状态为rejected,拒绝原因reson为类型错误的 promise 对象;

如果iterable对象为空,Promise.all会同步地返回一个状态为fulfilledvalue值为空数组的 promise 对象;

如果iterable对象不为空,一旦iterable中的某个 promise 对象完成或拒绝,返回的 promise 对象就会完成或拒绝,且返回的 promise 对象的value值(完成时)或reason值(拒绝时)和这个 promise 对象的value值(完成时)或reason值(拒绝时)相等。

    var promise1 = new Promise(function(resolve, reject) {
        setTimeout(resolve, 500, "one");
    }), promise2 = new Promise(function(resolve, reject) {
        setTimeout(resolve, 100, "two");
    });
    
    Promise.race([promise1, promise2]).then(function(value) {
      console.log(value);
      // Both resolve, but promise2 is faster
    });
    // expected output: "two"
实例方法

实例方法是定义在原型对象上的方法,声明实例方法:

Func.prototype.fn = function() {}

调用实例方法需要先创建一个实例:

let func = new Func();
func.fn(args);

Promise的原型对象上有3个实例方法:

Promise.prototype.then(onFulfilled, onRejected)

Promise.prototype.catch(onRejected)

Promise.prototype.finally(onFinally)

Promise.prototype.then(onFulfilled, onRejected):
then方法接收成功和失败两种情况的回调函数作为参数,返回一个Promise对象。

参数:onFulfilledonRejected回调函数。
onFulfilled:当 promise 对象变成 fulfilled 状态时被调用。onFulfilled函数有一个参数,即 promise 对象完成时的 value 值。如果onFulfilled不是函数,它会在then方法内部被替换成一个Identity函数,即 (x) => (x) 。
onRejected:当 promise 对象变成 rejected 状态时被调用。onRejected函数有一个参数,即 promise 对象失败时的 reason 值。如果onRejected不是函数,它会在then方法内部被替换成一个Thrower函数,即 (reason) => {throw reason} 。

返回:一旦调用then方法的 promise 对象被完成或拒绝,将异步调用相应的处理函数(onFulfilledonRejected),即将处理函数加入microtask队列中。如果onFulfilledonRejected回调函数:

返回一个值,则then返回的 promise 对象的status变为fulfilledvalue变为回调函数的返回值;

不返回任何内容,则then返回的 promise 对象的status变为fulfilledvalue变为undefined

抛出一个错误,则then返回的 promise 对象的status变为rejectedreason变为回调函数抛出的错误;

返回一个状态为fulfilled的 promise 对象,则then返回的 promise 对象的status变为fulfilledvalue等于回调函数返回的 promise 对象的value值;

返回一个状态为rejected的 promise 对象,则then返回的 promise 对象的status变为rejectedreason等于回调函数返回的 promise 对象的reason值;

返回一个状态为pending的 promise 对象,则then返回的 promise 对象的status变为pending,且其status将随着回调函数返回的 promise 对象的status变化而变化,之后其valuereason值也会和此 promise 对象的valuereason值相同。

这里提一下,这个地方看 MDN 文档中文翻译实在看不懂,之后看英文原文反而稍微理解了,希望之后在实现 Promise 的过程中能理解更深。

    var fromCallback;
    
    var fromThen = Promise.resolve("done")
    .then(function() {
        fromCallback = new Promise(function(){});
        return fromCallback;
    });
    
    setTimeout(function() {
        console.log(fromCallback);    //fromCallback.status === "pending"
        console.log(fromThen);        //fromThen.status === "pending"
        console.log(fromCallback === fromThen);    //false
    }, 0);

Promise.prototype.catch(onRejected):
catch方法接收失败情况的回调函数作为参数,返回一个Promise对象。
参数:onRejected回调函数,表现同then中的onRejected参数。
返回:promiseObj.catch(onRejected) 与 promiseObj.then(undefined, onRejected) 返回值相同。

    Promise.resolve()
      .then( () => {
        // 返回一个 rejected promise
        throw "Oh no!";
      })
      .catch( reason => {
        console.error( "onRejected function called: ", reason );
      })
      .then( () => {
        console.log( "I am always called even if the prior then"s promise rejects" );
      });

Promise.prototype.finally(onFinally):
finally方法接收onFinally回调函数作为参数,返回一个Promise对象。
如果你想在 promise 执行完毕后,无论其结果是成功还是失败,都做一些相同的处理时,可以使用finally方法。
参数:onFinally回调函数
onFinally不接收任何参数,当 promise 对象变成 settled (fulfilled / rejected) 状态时onFinally被调用。
返回:如果onFinally回调函数

不返回任何内容或者返回一个值或者返回一个状态为fulfilled的 promise 对象,则finally返回的 promise 对象的statusvaluereason值与调用这个finally方法的 promise 对象的值相同;

抛出一个错误或者返回一个状态为rejected的 promise 对象,则finally返回的 promise 对象的status值变为rejectedreason值变为被抛出的错误或者回调函数返回的 promise 对象的reason值;

返回一个状态为pending的 promise 对象,则finally返回的 promise 对象的status值变为pending,且其status值将随着回调函数返回的 promise 对象的status值变化而变化,之后其valuereason值也会和此 promise 对象的valuereason值相同。

Promise.reject("是我,开心吗").finally(function() {
    var pro = new Promise(function(r){r("你得不到我")});    //pro.status === "fulfilled"
    return pro;    //`onFinally`回调函数返回一个状态为`fulfilled`的 promise 对象
}).catch(function(reason) {
    console.log(reason);
});
结语

将 MDN 文档整理了一下,加入了一些自己的理解,也花费了一天的时间。现在Promise各个方法的参数、返回值、功能和使用方法已经有个大概的了解了,为了进一步理解其原理,接下来我打算简单地实现一下它。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102577.html

相关文章

  • Promise——阅读文档简单实现(二)

    摘要:在和方法执行的时候订阅事件,将自己的回调函数绑定到事件上,属性是发布者,一旦它的值发生改变就发布事件,执行回调函数。实现和方法的回调函数都是,当满足条件对象状态改变时,这些回调会被放入队列。所以我需要在某个变为时,删除它们绑定的回调函数。 前言 按照文档说明简单地实现 ES6 Promise的各个方法并不难,但是Promise的一些特殊需求实现起来并不简单,我首先提出一些不好实现或者容...

    dinfer 评论0 收藏0
  • ES6-7

    摘要:的翻译文档由的维护很多人说,阮老师已经有一本关于的书了入门,觉得看看这本书就足够了。前端的异步解决方案之和异步编程模式在前端开发过程中,显得越来越重要。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。 JavaScript Promise 迷你书(中文版) 超详细介绍promise的gitbook,看完再不会promise...... 本书的目的是以目前还在制定中的ECMASc...

    mudiyouyou 评论0 收藏0
  • 理解 Javascript 中的 Promise

    摘要:理解承诺有两个部分。如果异步操作成功,则通过的创建者调用函数返回预期结果,同样,如果出现意外错误,则通过调用函数传递错误具体信息。这将与理解对象密切相关。这个函数将创建一个,该将在到秒之间的随机数秒后执行或。 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! showImg(https://segmentfault.com/img/bVbkNvF?w=1280&h=...

    paulli3 评论0 收藏0
  • 理解 Javascript 中的 Promise

    摘要:理解承诺有两个部分。如果异步操作成功,则通过的创建者调用函数返回预期结果,同样,如果出现意外错误,则通过调用函数传递错误具体信息。这将与理解对象密切相关。这个函数将创建一个,该将在到秒之间的随机数秒后执行或。 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! showImg(https://segmentfault.com/img/bVbkNvF?w=1280&h=...

    chaos_G 评论0 收藏0
  • Node程序debug小记

    摘要:当前的部分代码状态超时再缩小了范围以后,进一步进行排查。函数是一个很简单的一次性函数,在第一次被触发时调用函数。因为上述使用的是,而非,所以在获取的时候,肯定为空,那么这就意味着会继续调用函数。 有时候,所见并不是所得,有些包,你需要去翻他的源码才知道为什么会这样。 背景 今天调试一个程序,用到了一个很久之前的NPM包,名为formstream,用来将form表单数据转换为流的形式进行...

    Achilles 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<