ECMAScript 6 学习:Promise篇

 

Promise是异步编程的一种解决方案。Promise对象,可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。

从0到1实现Promise 这篇文章详细地介绍了Promise的实现,值得细细研读。

 

 

1、Promise 新建后就会立即执行

 

2、调用 resolve 或 reject 并不会终结 Promise 的参数函数的执行

// 调用resolve(1)以后,后面的console.log(2)还是会执行,并且会首先打印出来
new Promise((resolve, reject) => {
  resolve(1);
  console.log(2);
}).then(r => {
  console.log(r);
});
// 2
// 1

 

3、Promise 内部的错误不会影响到 Promise 外部的代码

const someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    // 下面一行会报错,因为x没有声明
    resolve(x + 2);
  });
};

someAsyncThing().then(function() {
  console.log('everything is great');
});

setTimeout(() => { console.log(123) }, 2000);

// 浏览器打印出错误提示后,不会退出进程、终止脚本执行,2 秒之后还是会输出123

// Uncaught (in promise) ReferenceError: x is not defined
// 123

 

4、finally 方法的回调函数不接受任何参数,本质上是 then 方法的特例

finally是某些库对Promise实现的一个扩展方法,无论是resolve还是reject,都会走finally方法

下面是对finally方法的简单实现:

MyPromise.prototype.finally = function(fn) {
    return this.then(value => {
       fn();
       return value;
    }, reason => {
        fn();
        throw reason;
    });
};

finally 方法总是返回原来的值

// resolve 的值是 undefined
Promise.resolve(2).then(() => {}, () => {})

// resolve 的值是 2
Promise.resolve(2).finally(() => {})

// reject 的值是 undefined
Promise.reject(3).then(() => {}, () => {})

// reject 的值是 3
Promise.reject(3).finally(() => {})

 

5、Promise.reject()方法的参数,会原封不动地作为 reject 的理由,变成后续方法的参数

// Promise.reject方法的参数是一个thenable对象
// 执行以后,后面catch方法的参数不是reject抛出的“出错了”这个字符串,而是thenable对象
const thenable = {
  then(resolve, reject) {
    reject('出错了');
  }
};

Promise.reject(thenable)
.catch(e => {
  console.log(e === thenable)
})
// true

 

6、Generator 函数与 Promise 的结合

function getFoo() {
    // console.log('执行getFoo');
    return new Promise(function (resolve, reject) {
        resolve('foo');
    });
}

const g = function* () {
    try {
        // next方法将指针移到getFoo()才执行函数getFoo
        // 第一次调用next方法,执行到getFoo()结束,此时还没有给foo赋值
        console.log(111);
        const foo = yield getFoo();
        // yield表达式本身没有返回值,或者说总是返回undefined。
        // next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值
        // 第二次调用next方法(此时带参数'foo'),才给foo赋值,并打印foo
        console.log(foo);
    } catch (e) {
        console.log(e);
    }
};

function run(generator) {
    const it = generator(); // 生成遍历器对象

    function go(result) {
        // console.log('result', result);
        if (result.done) return result.value;

        return result.value.then(function (value) {
            // console.log('value', value);
            return go(it.next(value));
        }, function (error) {
            return go(it.throw(error));
        });
    }

    go(it.next());
}

run(g);

 

7、如何停止一个Promise链

假设这样一个场景,我们有一个很长的Promise链式调用,这些Promise是依次依赖的关系,如果链条中的某个Promise出错了,就不需要再向下执行了,默认情况下,我们是无法实现这个需求的,因为Promise无论是then还是catch都会返回一个Promise,都会继续向下执行thencatch。举例:

new Promise(function(resolve, reject) {
  resolve(1111)
}).then(function(value) {
  // "ERROR!!!"
}).catch()
  .then()
  .then()
  .catch()
  .then()

有没有办法让这个链式调用在ERROR!!!的后面就停掉,完全不去执行链式调用后面所有回调函数呢?

我们自己封装一个Promise.stop方法。

MyPromise.stop = function() {
  return new Promise(function() {});
};

stop中返回一个永远不执行resolve或者rejectPromise,那么这个Promise永远处于pending状态,所以永远也不会向下执行thencatch了。这样我们就停止了一个Promise链。

new MyPromise(function(resolve, reject) {
  resolve(1111)
}).then(function(value) {
  // "ERROR!!!"
  MyPromise.stop();
}).catch()
  .then()
  .then()
  .catch()
  .then()

但是这样会有一个缺点,就是链式调用后面的所有回调函数都无法被垃圾回收器回收。

 

8、如何解决Promise链上返回的最后一个Promise出现错误

看如下例子:

new Promise(function(resolve) {
  resolve(42)
}).then(function(value) {
  a.b = 2;
});

这里a不存在,所以给a.b赋值是一个语法错误,onFulfilled回调函数是包在try...catch中执行的,错误会被catch到,但是由于后面没有thencatch了,这个错误无法被处理,就会被Promise吃掉,没有任何异常,这就是常说的Promise有可能会吃掉错误

那么我们怎么处理这种情况呢?

方法一

使用done()

new Promise(function(resolve) {
  resolve(42)
}).then(function(value) {
  a.b = 2;
}).done();

done()方法相当于一个catch,但是却不再返回Promise了,注意done()方法中不能出现语法错误,否则又无法捕获了。

done方法作为Promise链式调用的最后一步,用来向全局抛出没有被Promise内部捕获的错误,并且不再返回一个Promise。一般用来结束一个Promise链。

下面是done()方法的简单实现:

MyPromise.prototype.done = function() {
    this.catch(reason => {
        console.log('done', reason);
        throw reason;
    });
};

 

方法二

普通错误监听windowerror事件可以实现捕获

window.addEventListener('error', error => {
  console.log(error); // 不会触发
});

Promise没有被onRejected()处理的错误需要监听unhandledrejection事件

window.addEventListener('unhandledrejection', error=>
{
    console.log(error); // 打印"Hello, Fundebug!"
});
 
function foo()
{
    Promise.reject('Hello, Fundebug!');
}
 
foo();

 

PromiseA+规范

JavaScript Promise迷你书(中文版)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值