Javascript异步编程之Promise

作者: 黎贝卡beka | 来源:发表于2018-03-07 10:55 被阅读0次

异步回调的问题

  • 层次嵌套很深,难以维护
  • 无法正常的使用return和throw
  • 无法正常的检索堆栈信息 (每次回调执行都是在系统层面上新的堆栈)
  • 多个回调之间难以建立联系

Promise的设计

new Promise(
    /* 执行器 executor */
    function (resolve, reject) {
        /* 一段耗时很长的异步操作 */
    resolve(); // 数据处理完成
    reject(); // 数据处理出错
    }
)
.then(function A() {
   // 成功,下一步
 }, function B() {
   // 失败,做相应处理
});
  • Promise是一个代理对象,它和原先要进行的异步操作并无关系,只是把这段耗时很长的异步操作放在执行器中;
  • 通过引入一个回调,避免更多的回调;
  • Promise有3个状态:
    -pending [待定] 初始状态
    -fulfilled [实现] 操作成功
    -rejected [被否决] 操作失败
  • 当Promise状态一旦发生改变,就会触发对应.then里的响应函数来处理后续步骤;
  • Promise状态一经改变,就不会再变;
  • Promise实例一经创建,执行器立即执行
    Promise.png
  1. 定时执行
console.log('here we go');
new Promise( resolve => {
    setTimeout( () => {
        resolve('hello');
    }, 2000);
})
.then( value => {
    console.log( value + ' world');
});

输出结果:
here we go
Promise {<pending>}
(约2s后)hello world

  1. 分两次,依次定时执行
console.log('here we go');
new Promise( resolve => {
    setTimeout( () => {
        resolve('hello');
    }, 2000);
})
.then( value => {
    console.log(value);
    return new Promise( resolve => {
        setTimeout( () => {
            resolve('world');
         }, 2000);
    });
})
.then( value => {
      console.log( value + ' world');
});

输出结果:
here we go
Promise {<pending>}
(约2s后)hello
(约2s后)world world

  1. 对已完成的Promise执行.then
console.log('start');

let promise = new Promise(resolve => {
    setTimeout(() => {
        console.log('the promise fulfilled');
        resolve('hello, world');
    }, 1000);
});

setTimeout(() => {
    promise.then( value => {
        console.log(value);
    });
}, 3000);

输出结果:
start
74
(约1s后)the promise fulfilled
(约2s后)hello, world

在任何地方生成一个Promise队列后,可以把它作为一个变量传到其他地方,不管这个Promise状态是否是完成状态,队列都会依次执行;若为完成,会依次执行Promise内的执行器部分,若已完成,后面追加的.then将会得到Promise返回的值

  1. then()里不返回Promise
console.log('here we go');
new Promise(resolve => {
    setTimeout( () => {
        resolve('hello');
    }, 2000);
})
.then( value => {
     console.log(value);
     console.log('everyone');
     (function () {
         return new Promise(resolve => {
            setTimeout(() => {
                console.log('Mr.Laurence');
                resolve('Merry Xmas');
             }, 2000);
         });
    }());
    return false;
})
.then( value => {
     console.log(value + ' world');
});

输出结果:
here we go
Promise {<pending>}
hello
everyone
false world
(约2s后)Mr.Laurence

在这里,第一个then()里相应函数返回的Promise并不会等待新创建的Promise实例执行(那段立即函数,陷阱!),即使then()响应函数返回值是false;也就是说,在Promise里即使没有直接返回一个Promise实例,也会默认去执行下一个环节。这里将return false注释掉,可以发现,会默认返回undefined值,并传递给下一个then()的相应函数中。

下面介绍一下.then()

  • 接收两个函数作为参数,分别代表fulfilledrejected
  • .then()返回一个新的Promise实例,所以它可以链式调用
  • 当前面的Promise状态改变时,.then()会根据其最终状态选择相应的响应函数进行处理
  • 状态响应函数可以返回一个新的Promise实例,或其它值,不返回则为undefined
  • 如果返回新的Promise实例,那么下一级.then()会在新Promise状态发生改变后执行
  • 如果返回其它值,则会立刻执行下一级.then()

避免嵌套.then()

因为.then()返回的还是Promise实例,会等里面的.then()执行完,才执行外面的。对于我们来说,此时最好将其展开,避免then()函数里嵌套then(),也方便阅读。

四道题目

原问题地址译文地址请戳←
假设doSomething和doSomethingElse返回的都是一个Promise实例,下面的四种 promises 的区别是什么?

  1. 问题一
doSomething()
    .then(function () {
        return doSomethingElse();
    })
    .then(finalHandler);

答案:
(最常见的Promise形式,then()相应函数返回一个新的Promise实例)

// doSomething
// |-----------|
//             doSomethingElse(undefined)
//             |------------|
//                          finalHandler(resultOfDoSomethingElse)
//                          |------------|
  1. 问题二
doSomething()
    .then(function () {
        doSomethingElse();
    })
    .then(finalHandler);

答案:
(第一个then()响应函数没有直接返回一个新的Promise实例,也就是说doSomethingElse()返回的Promise实例并没有返回给then()的响应函数,此时默认返回undefined,并且finalHandler会立刻执行几乎会和doSomethingElse ()同时)

// doSomething
// |------------------|
//                    doSomethingElse(undefined)
//                    |------------------|
//                    finalHandler(undefined)
//                    |------------------|
  1. 问题三
doSomething()
    .then(doSomethingElse())
    .then(finalHandler);

答案:
(第一个then()函数中传入的是一个函数,且采用的是函数执行的方式;实际上传入的是一个Promise实例。这种情况下,doSomethingElsedoSomething的执行可以看作几乎是同时的,因为是在同一个栈中执行的)。

在Promise的规范中,then()中传入的若不是一个函数,这个then()就会被忽略;在这里doSomethingElse执行完返回的是一个Promise。也就是说,finalHandler监听的是doSomething的完成时间,在doSomething完成后,finalHandler就会被执行。

// doSomething
// |------------------|
// doSomethingElse(undefined)
// |----------------------------------|
//                    finalHandler(resultOfDoSomething)
//                    |------------------|
  1. 问题四
doSomething()
    .then(doSomethingElse)
    .then(finalHandler);

答案:
then()接收两个函数作为参数,第一个是fulfilled状态,第二个是rejected状态);在这里doSomethingElse作为fulfilled状态的响应函数对其进行后续处理

// doSomething
// |-----------|
//             doSomethingElse(resultOfDoSomething)
//             |------------|
//                         finalHandler(resultOfDoSomethingElse)
//                         |------------------|

关于错误处理

Promise会自动捕获内部异常(即在执行器当中如果发生错误或自己抛出错误,Promise的状态就会被改为rejected),随后调用rejected响应函数进行处理,也会向后面寻找catch()响应函数进行处理。

  1. catch捕获
console.log('here we go');
new Promise( resolve => {
    setTimeout( () => {
        throw new Error('bye');
    }, 2000);
})
    .then( value => {
        console.log( value + ' world');
    })
    .catch( error => {
        console.log( 'Error:', error.message);
    });

输出结果:(fulfilled响应函数不会得到执行)
here we go
Promise {<pending>}
(约2s后)Uncaught Error: bye
at setTimeout (<anonymous>:4:15)

  1. reject响应捕获
console.log('here we go');
new Promise( (resolve, reject) => {
    setTimeout( () => {
        reject('bye');//throw new Error('bye');
    }, 2000);
})
    .then( value => {
        console.log( value + ' world');
    }, value => {
        console.log( 'Error:', value);
    });

推荐使用第一种,更加清晰好读,并且可以捕获前面所有.then()的错误。

关于catch()

实际上只是 then(null, ...)的语法糖

console.log('here we go');

new Promise(resolve => {
    setTimeout(() => {
        resolve();
    }, 1000);
})
    .then( () => {
        console.log('start');
        throw new Error('test error');
    })
    .catch( err => {
        console.log('I catch:', err);

        // 下面这一行的注释将引发不同的走向
        // throw new Error('another error');
    })
    .then( () => {
        console.log('arrive here');
    })
    .then( () => {
        console.log('... and here');
    })
    .catch( err => {
        console.log('No, I catch:', err);
    });

输出结果:
here we go
Promise {<pending>}
(约1s后)start
I catch: Error: test error
at Promise.then (<anonymous>:10:15)
at <anonymous>
arrive here
and here

可以看出,catch()也返回一个Promise实例,并且catch()内部没有抛出错误时,返回的这个Promise实例是fulfilled状态,所以接下来的then()都会被执行。

注意:

建议在所有队列最后都加上catch(),以避免漏掉错误处理造成意想不到的问题。

References

[翻译] We have a problem with promises

相关文章

  • Javascript异步编程之Promise

    异步回调的问题 层次嵌套很深,难以维护 无法正常的使用return和throw 无法正常的检索堆栈信息 (每次回调...

  • ES6 之 Promise

    Promise是JavaScript异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步...

  • 【ECMAScript6】 Promise

    Promise 是什么 Promise 是 ES6 为 JavaScript 异步编程定义的全新 api 。为异步...

  • part1整理

    函数式编程:JavaScript函数式编程指南 异步编程:异步编程 Promise源码 JavaScript基础知...

  • 深入了解下Promise

    Promise 意义 Promise 的诞生与 Javascript 中异步编程息息相关,js 中异步编程主要指 ...

  • 【Dart】异步函数

    异步函数 JavaScript中,异步调用通过Promise来实现async函数返回一个Promise。await...

  • Java实现Promise.all()

    JavaScript的Promise.all() Promise是JavaScript异步编程的一种解决方案,在E...

  • Promise对象

    Promise 是 JavaScript 异步操作解决方案。介绍 Promise 之前,先对异步操作做一个详细介绍...

  • javascript 异步(一)

    在开始谈javascript异步编程之前,我们先抛出几个问题,针对问题回答问题,从而解决问题 1、为什么要有异步编...

  • Promise介绍及jQuery deferred详解

    Promise介绍 Promise 是 JavaScript 的异步操作解决方案,是ES6提出的异步操作解决方案之...

网友评论

    本文标题:Javascript异步编程之Promise

    本文链接:https://www.haomeiwen.com/subject/vwzrfftx.html