Promise从入门到理解

542 阅读4分钟

为什么要有Promise

由于js本身的单线程机制,导致js的网络操作等必须通过异步执行,而早期的异步执行方式都是通过callback(即回调函数)完成的,例如:

function callback() {
    console.log('回调函数执行');
}
console.log('开始异步前');
setTimeout(callback, 1000); // 1秒钟后调用callback函数
console.log('开始异步后');

//观察输出为
开始异步前
开始异步后
(等待1秒后)
回调函数执行

当业务逻辑需要我们执行一连串的回调时,代码可能像下面这样:

function callback(count, fun) {
    setTimeout(function () {
        console.log('回调成功' + count + '次');
        fun(count);
    }, 1000);
}

callback(1, function (count) {
    callback(count + 1, function (count) {
        callback(count + 1, function (count) {
            callback(count + 1, function (count) {
                callback(count + 1, function (count) {
                    console.log(count + '次回调结束');
                })
            })
        })
    })
})

//观察输出为
(等待1秒后)
回调成功1次
(等待1秒后)
回调成功2次
(等待1秒后)
回调成功3次
(等待1秒后)
回调成功4次
(等待1秒后)
回调成功5次
5次回调结束

这段代码就是臭名昭著的邪恶金字塔(Pyramid of Doom),当异步的任务很多的时候,维护大量的callback将是一场灾难。

想象一下如果在callback之内必须要执行不同的处理逻辑,比如success和fail:

function callback(mark, options) {
    var options = options || {}
    var successFun = options.success;
    var failFun = options.fail;
    setTimeout(function () {
        if (mark) {
            successFun('success');
        } else {
            failFun('fail');
        }
    }, 1000);
}

callback(true, {
    success: function (data) {
        console.log(data);
        callback(true, {
            success: function (data) {
                console.log(data);
                callback(false, {
                    success: function (data) {
                        console.log(data);
                    },
                    fail: function (data) {
                        console.log(data);
                    }
                })
            },
            fail: function (data) {
                console.log(data);
            }
        })
    },
    fail: function (data) {
        console.log(data);
    }
})

//观察输出为
(等待1秒后)
success
(等待1秒后)
success
(等待1秒后)
fail

这样的代码看起来简直一团糟,可读性极差,是不是能有一种更简介明了的处理异步方法时序的写法呢,于是乎 Promise横空出世了

什么是Promise

所谓Promise,字面上可以理解为“承诺”,就是说A调用B,B返回一个“承诺”给A,A就可以根据B的"承诺"决定自己要执行什么逻辑,比如当B返回想要的结果时,A执行planA,否则执行planB。如果后续还有planC、palnD等依赖planA的结果,可以继续在planA上进行一个"承诺",携程代码的结构大致如下:

var A = function() {
    B.then(planA, planB)
     .then(planC, planD)
     .then(planE, planF)
}
A();

这种链式写法的好处在于,逻辑结构清晰明朗,一眼看出谁先谁后

Promise基本概念

Promise在ES6中被统一规范,目前许多高级浏览器已经直接支持(Chrome 32、Opera 19、Firefox 29、Safari 8 和 Microsoft Edge 起,promise 默认启用),且有许多开源库的实现,例如:

先来认识下Promise/A+规范:

  • 一个promise可能有三种状态:等待(pending)、已完成(fulfilled)、已拒绝(rejected)

  • 一个promise的状态只可能从“等待”转到“完成”态或者“拒绝”态,不能逆向转换,同时“完成”态和“拒绝”态不能相互转换

  • promise必须实现then方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致

  • then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用。同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。

用Promise编写一个需要串行执行的异步计算代码

function plus(input) {
    return new Promise((resolve, reject) => {
        console.log('执行:' + input + ' + ' + input);
        setTimeout(resolve, 1000, input + input);
    });
}

function multiply(input) {
    return new Promise((resolve, reject) => {
        console.log('执行:' + input + ' * ' + input);
        setTimeout(resolve, 1000, input * input);
    });
}

var arithmetic = new Promise((resolve, reject) => {
    console.log('开始进行计算:');
    resolve(6)
});

arithmetic.then(multiply)
    .then(plus)
    .then(multiply)
    .then(plus)
    .then(function (result) {
        console.log('计算结果:' + result);
    });
    
//观察输出为
开始进行计算:
(等待1秒后)
执行:6 * 6
(等待1秒后)
执行:36 + 36
(等待1秒后)
执行:72 * 72
(等待1秒后)
执行:5184 + 5184
(等待1秒后)
计算结果:10368

用Promise重写之前的callback

function callback(mark) {
    return new Promise((resolve, reject) => {
        setTimeout(function () {
            if (mark) {
                resolve('success');
            } else {
                reject('fail');
            }
        }, 1000);
    })
}

callback(true).then(function (success) {
    console.log(success);
    return callback(true);
}, function (fail) {
    console.log(fail);
}).then(function (success) {
    console.log(success);
    return callback(false);
}, function (fail) {
    console.log(fail);
}).then(function (success) {
    console.log(success);
}, function (fail) {
    console.log(fail);
})

//观察输出为
(等待1秒后)
success
(等待1秒后)
success
(等待1秒后)
fail

then 执行后会 return 一个新的 Promise 对象,以实现链式调用,你可以显式的 return 一个 Promise 对象,若不显式的 return 则会 return 一个以 undefined 值 resolve 的 Promise 对象。 返回任意一个非 promise 的值都会被包裹成 promise 对象,即 return 1 等价于 return Promise.resolve(1)。

示例如下:

callback(true).then(function (success) {
    console.log(success);
}, function (fail) {
    console.log(fail);
}).then(function (success) {
    console.log(success);
}, function (fail) {
    console.log(fail);
}).then(function (success) {
    console.log(success);
}, function (fail) {
    console.log(fail);
})

//观察输出为
(等待1秒后)
success
undefined
undefined

更加详细的解释和示例可参考: Javascript Promise 简介