js 异步系列(3) -promise中

上一节promise上已经根据promise/A+规范写出了基本的Promise类的基本框架。

那么在promise/A+规范中还有几个关于then中的规范没有实现。

  1. 如果返回一个普通值,会走下一个then的成功回调
  2. 如果返回一个promise,会将他执行完的状态给下一个then
  3. 如果抛出错误,会走下一个then的reject方法
  4. 返回一个新的promise。实现链式调用

1. 返回一个新的promise。实现链式调用

    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
        onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err};
        // 因为then中的回调函数是异步执行的。为了确保newPromise存在,需要setTimeout
        let newPromise = new Promise((resolve, reject) => {
            setTimeout(() => {
                if (this.status === FULFILLED) {
                    // 如果执行then中报错,需要直接reject
                    try {
                         // 执行下then中的方法的返回值当成下一个then的参数传递
                        let x = onFulfilled(this.value);
                        // 返回值有多种情况。普通值或者还是一个promise
                        resolvePromise(newPromise, x, resolve, reject);
                    } catch(err) {
                        reject(err)
                    } 
                }

                if (this.status === REJECTED) {
                    // 如果失败的then中有报错或者还返回一个promise直接向后传递即可
                    let x = onRejected(this.reason);
                    reject(x);
                }

                if (this.status === PENDING) {
                    this.onResolvedCallbacks.push(() => {
                        try {
                            let x = onFulfilled(this.value);
                            resolvePromise(newPromise, x, resolve, reject);
                        } catch(err) {
                            reject(err);
                        }
                    })
                    this.onRejectedCallbacks.push(() => {
                        let x = onRejected(this.reason);
                        reject(x);
                    })
                }
            })
        })
        return newPromise;
    }

2. 如果then中执行后返回的还是一个promise

定义resolvePromise 函数来分情况处理then中返回的结果

const resolvePromise = (promise2, x, resolve, reject) => {
  // 处理x的类型来决定下次then的状态是resolve还是reject
  // promise2 === x的情况就相当于
  // new Promise(resolve => resolve(1)).then(res => a1);
  if (promise2 === x) {
    return reject(new TypeError(`Chaining cycle detected for promise #<Promise>`));
  }
  let called = false;
  // 判断x是不是一个普通函数 
  if (typeof x === 'object' && x !== null || typeof x === 'function') {
    // 判断是否有then方法来判断是不是promise 
    try {
      let then = x.then;
      if (typeof then === 'function') {
        // 是promise情况 使用then.call来执行x.then方法是为了避免有的对象写的只能获取一次。
        // x.then需要再获取一次 而then.call是上次的缓存
        then.call(x, y => {
          // 参数可能还是promise需要递归
          // .then(res => new Promise(resolve => resolve(new Promise..)))
          resolvePromise(promise2, y, resolve, reject);
        }, r => {
          if (called) return; // 防止多次调用
          called = true;
          reject(r)
        })
      } else { // [1,2,3] {a:1}
        resolve(x);
      }
    } catch(err) {
      if (called) return; // 防止多次调用
      called = true;
      reject(err);
    }
  } else {
    // 不是对象或者函数 普通值
    resolve(x);
  }
}

返回值为promise

如果then中返回promise,也是需要等待这个新的promise执行完毕。直到返回一个resolve()为普通值的promise为止。

new Promise(resolve => resolve(1)).then(res => {
  return new Promise(resolve => resolve(res))
}).then(res => {console.log(2)});

// 所以return 一个new promise,也是要先调用它的then方法返回一个新的promise
// 返回的新的promise继续then讲返回值向后抛
// 和resolve(Promise...) 一样 都是相当于加了2个then
new Promise(resolve => resolve(1))
.then(res => res)
.then(res => res).then(res => res)  // 这两个就相当于新增的2个
.then(res => {
  console.log(2)
});

如果promise当作resolve()的参数或者在then中直接返回。则相当于多加了2次then

Promise.resolve() 和 Promise.reject()

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'));

但是参数是分4种情况的:

(1)参数是一个 Promise 实例

如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。

// resolve()参数还是promise则相当于多加两个then
const p = new Promise(resovle => setTimeout(resovle));
new Promise(resolve => resolve(p)).then(() => {
  console.log("3");
});
p.then(() => {
  console.log("1");
}).then(() => {
  console.log("2");
});
// 1 2 3

// 而如果Promise.resolve() 参数中的Promise则是直接替换
const p = new Promise(resovle => setTimeout(resovle));
Promise.resolve(p).then(() => {
  console.log("3");
});
p.then(() => {
  console.log("1");
}).then(() => {
  console.log("2");
});
// 3 1 2

(2)参数是一个thenable对象

thenable对象指的是具有then方法的对象,比如下面这个对象。

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});

上面代码中,thenable对象的then方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then方法指定的回调函数,输出 42。

(3)参数不是具有then方法的对象,或根本就不是对象

如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。

const p = Promise.resolve('Hello');

p.then(function (s){
  console.log(s)
});
// Hello

等价于new Promise(resolve => resolve('Hello'));

(4)不带有任何参数

Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用Promise.resolve()方法。

const p = Promise.resolve();

p.then(function (res) {
  // res undefined
});

上面代码的变量p就是一个 Promise 对象。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容