Promise

Promise 是什么?

词语本意:
发音:[ˈprɒmɪs]
词性:名词,
翻译:许诺,允诺。
MDN解释
  1. Promise 对象用于一个异步操作。
  2. 一个Promise表示一个现在,将来或永不可能可用的值。
按照书写方式来解释

可以将异步操作的书写方式变成一种队列化的书写方式,并按照我们写的那样的预期来执行返回符合预期的结果。

为什么需要Promise

js为检查表单而生,其首要目标是操作dom,界面冻结会造成非常不友好的用户体验,所以dom操作大多是异步的。
注意:同步操作的处理器未执行完成会使得界面无法响应用户的其他操作,而异步操作则可以避免该问题。
常见的异步操作语法:

1. 异步事件
$('#start').on('click',startHandler);
function startHandler(){} // 定义事件响应的处理器函数

用户点击start元素就会触发一个异步事件去执行,再次触发不必等待前一次事件执行完成,就可以再次触发。

2. 回调函数
$.ajax('http://baidu.com',{
    success:function(res){
    // 成功回调处理器
    },
    error:function(e){
    // 失败回调处理器
    }
});

用户发送一个向百度服务器获取数据的异步请求

3. 无阻塞高并发的node的诞生

更加严重的依赖异步操作才能完成无阻赛高并发的特性。

然而异步回调这种方式并不是理想的,它却有一些问题,
比如:

  1. 回调地狱这种难于维护的书写方式。

更深层次的问题:

  1. 嵌套回调中的循环语句出错后,无法正常的使用return和throw终止
  2. 无法正常检索堆栈信息(每一个回调都会重新建立堆栈)
  3. 多个回调之间难以建立联系

Promise详解

一个promise的基本使用案例:

new Promise(
    // 执行器 executor
    function ( resolve, reject ) {
        //一段耗时很长的异步操作
        resolve(); //异步处理成功
        reject(); // 异步处理失败 
        //注意:resolve和reject这俩个回调函数被调用是互斥的  
    }
).then( function A(){
            //成功,下一步
    }, 
    function B(){
        //失败,做善后处理
    } 
);
  1. promise是一个代理对象,它和原先要进行的操作并无关系。
  2. 它通过引入一个回调,避免更多的回调。
promise有三个状态:

pending 初始状态 fulfilled 操作成功 rejectd 操作失败

  1. promise一经创建,执行器便会立刻执行;
  2. 异步操作通过调用resolve改变promise状态为fulfilled,通过调用reject改变promise状态为rejected;
  3. promise状态发生改变就会触发then里面的响应函数;
  4. promise状态一经改变,不会再变。
promise最简单的实现:
//范例1
console.log('here we go');
new Promise( resolve => {
    setTimeout( () => {
        resolve('hello');
    }, 2000);
}).then( value => {
    console.log( value + ' world');
});
/* 控制台日志 */ 
here we go
// 2s以后
hello world
// 范例2
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
// 2s后
hello
// 2s后
world world

// 假如一个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);
/* 控制台 */
17:37:29.015 start
17:37:30.016 the promise fulfilled
17:37:32.017 hello, world

// 假如在.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'); return 0
}).then( value => {
    console.log(value + ' world');
}).then( value => {
    console.log(value + ' world');
});
/* 控制台 */
17:53:03.175 here we go
17:53:03.191 Promise {<pending>}
17:53:05.181 hello
17:53:05.181 everyone
17:53:05.181 undefined world
17:53:05.182 0 world
17:53:05.182 undefined world
17:53:07.187 Mr.Laurence
从日志可以看出,
1. 执行器中必须回调resolve或者reject改变promise的状态;
2. 当状态改变后继续调用then方法,但是promise中的状态已经无法改变了;
3. 可以看到promise对象帮我们生成了一个队列,这个队列一直存在。
then方法
  1. then() 接受俩个状态响应函数参数,分别代表fulfilled和rejected
  2. then() 返回一个新的Promise实例,所以它可以链式调用
  3. 状态响应函数可以返回新的Promise,其他值,或者不返回值
  4. 如果返回新的Promise,那么下一级.then()会在新的promise状态改变之后执行
  5. 如果返回其它任何值,则会立刻执行下一级.then()
  6. then里面有then的情况
    1). 因为.then()返回的还是Promise实例。
    2). 会等里面的.then()执行完,在执行外面的。
    3). 对我们来说,此时最好将其展开,会更好阅读。

测试
问题:下面四种Promise的区别是什么?

前提:doSomething方法和doSomethingElse方法均返回一个promise实例。
// #1
doSomething().then(function() {
    return doSomethingElse();
});
// #2
doSomething().then(function(){
    doSomethingElse();
});
// #3
doSomething().then(doSomethingElse());
// #4
doSomething().then(doSomethingElse);
// 解析:
// #1
doSomething().then(function() {
    return doSomethingElse();
});
//符合预期的,可以继续回调then方法获取doSomethingElse的结果
// #2
doSomething().then(function(){
    doSomethingElse();
});
//then中没有返回promise,所以之后的then无法获取到doSomethingElse的结果,并且与doSomethingElse几乎同时执行
// #3
doSomething().then(doSomethingElse());
// then应该接收一个状态处理函数,当前这种情况传入了一个promise实例,无法获取上层promise的结果,而该层then会被忽略,下一层会获取到doSomething()的结果
// #4
doSomething().then(doSomethingElse);
// then方法中吧doSomethingElse直接当成状态处理函数也是可以达到预期的
错误处理 catch方法

catch和then一样会返回一个promise实例。如果没有抛出错误,该实例默认也会返回fulfilled状态。

Promise 常用函数

promise.all
接收一个数组参数,返回一个新的promise实例,响应函数中的结果为数组中的每个promise的结果。
与map连用
使用forEach和reduce实现队列
ES2017 的async/await

该学习笔记是学习慕课网中 Promise入门 记录


言月
1.8k 声望490 粉丝

从有技术广度到技术深度的转变,这样才能被自己迷恋