天天看点

JavaScript-Promise基础知识

Promise是ES6中新增的一个类,专门用来解决异步回调地狱的问题,异步代码同步显示出来。

回调函数的例子

//成功回调函数
function successCallback() { // 固定格式
    console.log('SUCCESS')
}
//失败回调函数
function failureCallback() { // 固定格式
    console.log('FAILURE')
}

function myFunc(param, success, failure) {
    if (param) {
        success()
    } else {
        failure()
    }
}

myFunc(0, successCallback, failureCallback) // FAILURE
myFunc(1, successCallback, failureCallback) // SUCCESS      

以上回调函数可用promise实现

var t = 0;
var promise = new Promise(function(resolve, reject) {
    if (t%2 == 0) {
        resolve();
    } else {
        reject();
    }
});
promise.then(function() {
    console.log('SUCCESS');
}, function(){
    console.log('FAILURE');
});

运行结果:

SUCCESS      

注:new了一个Promise对象,没调用它,函数就已经执行了。所以使用Promise时一般包在一个函数中,在需要时调用。

因此,上面的实例可以包在函数fn()里面,需要时就调用fn()

function fn() {
    var t = 1;
    var promise = new Promise(function(resolve, reject) {
        if (t%2 == 0) {
            console.log(1);
            resolve();
            console.log(1.1);
        } else {
            console.log(2);
            reject();
            console.log(2.1);
        }
    });
    promise.then(function() {
        console.log(3);
        console.log('SUCCESS');
        console.log(3.1);
    }, function(){
        console.log(4);
        console.log('FAILURE');
        console.log(4.1);
    });

    return promise;
}
fn();      
运行结果:

2
2.1
4
FAILURE
4.1      

Promise运行流程

JavaScript-Promise基础知识
JavaScript-Promise基础知识
function test_01() {
    new Promise(function(resolve, reject) {
        setTimeout(() => {
            resolve('success....'),
            reject('fail...')
        }, 1000);
    }).then(
        value => {
            console.log('onResolved()1', value);
        }
    ).catch(
        reason => {
            console.log('onRejected()1', reason);
        }
    )
}
test_01();      

输出结果:onResolved()1 success....

setTimeout中resolve/reject只能执行一个,要么成功、要么失败。

function test_02() {
    // 产生一个成功值为1的promise对象
    const p1 = new Promise(function(resolve, reject) {
        resolve(1)
    })
    // 产生一个成功值为2的promise对象
    const p2 = Promise.resolve(2);
    // 产生一个失败值为3的promise对象
    const p3 = Promise.reject(3);

    p1.then(value => {console.log(value)})
    p2.then(value => {console.log(value)})
    // p3.then(null, reason => {console.log(reason)}) // 写法1
    // p3.then(null, value => {console.log(value)}) // 写法2
    // p3.catch(value => {console.log(value)}) // 写法3
    p3.catch(reason => {console.log(reason)}) // 写法4
}
test_02();      

输出结果:1 2 3

function test_01() {
    new Promise(resolve => {
        console.log(123);
    }).then(value => {console.log(456)})
    console.log("hi")
}
test_01();      

执行结果:123 hi

注:then没有被执行,是因为没有resolve通知。只有得到通知才会进入微任务队列。

Promise异常捕获

Promise.prototype.catch方法是Promise.prototype.then(null, rejection)的别名,用于指定发生错误时的回调函数。

Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

getJSON("/visa.json").then(function(json) {
return json.name;
}).then(function(name) {
// proceed
}).catch(function(error) {
//处理前面任一个then函数抛出的错误
});      
<script>
function test_01() {
    const p = new Promise(function(resolve, reject) {
        setTimeout(() => {
            // resolve('success....'), // promise变为resolved 成功状态
            // reject('fail...') // promise变为rejected 失败状态
            throw new Error('error....') // 抛出异常,promise变为pending状态,value为'error...'
        }, 1000);
    });
    console.log(p);
}
test_01();
</script>      
JavaScript-Promise基础知识

先改状态,还是先改值?

function test_03() { // 常规:先执行回调函数,后改状态
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1); // 2. 改变状态(pending → resolved, 同时调用回调函数指定数据)setTimeout为宏任务
        },1000)
    }).then( // 1. 执行回调函数(微任务),保存当前指定的回调函数
        value => {},
        reason => {}
    )
}      
function test_04() {// 先改状态,后执行回调函数
    new Promise((resolve, reject) => {
        resolve(1); // 1. 改变状态(pending → resolved, 同时调用回调函数指定数据)
    }).then( // 2. 后执行回调函数(微任务),异步执行回调函数
        value => {},
        reason => {}
    )
}      

Promise状态改变是不可逆的,pending → resolved之后,无法再转为其他状态。

因此,resolve / reject 只能执行一个。

Promise链式调用

  • 回调地狱
  • 代码难阅读
  • 多个串联函数适合使用回调函数嵌套

链式Promise是指在当前promise达到fulfilled状态后,即开始进行下一个promise(后邻promise)。如何衔接当前promise和后邻promise呢?(这是这里的难点)。只要在then方法里面return一个promise就好了(得到一个promise是前提)。

注意: 一定要有返回值,否则,callback 将无法获取上一个 Promise 的结果。

(如果使用箭头函数,() => x 比 () => { return x; } 更简洁一些,但后一种保留 return 的写法才支持使用多个语句。)。

function runAsync1(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务1执行完成');
            resolve('随便什么数据1');
        }, 1000);
    });
    return p;            
}
function runAsync2(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务2执行完成');
            resolve('随便什么数据2');
        }, 2000);
    });
    return p;            
}
function runAsync3(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务3执行完成');
            resolve('随便什么数据3');
        }, 2000);
    });
    return p;            
}

runAsync1()
.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
    console.log(data);
    return runAsync3();
})
.then(function(data){
    console.log(data);
});

运行结果:

异步任务1执行完成
随便什么数据1
异步任务2执行完成
随便什么数据2
异步任务3执行完成
随便什么数据3      

如果then不返回Promise对象,则后面then不再受到数据。

function runAsync1(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务1执行完成');
            resolve('随便什么数据1');
        }, 1000);
    });
    return p;            
}
function runAsync2(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务2执行完成');
            resolve('随便什么数据2');
        }, 2000);
    });
    return p;            
}
function runAsync3(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务3执行完成');
            resolve('随便什么数据3');
        }, 2000);
    });
    return p;            
}

runAsync1()
.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
    console.log(data);
    return '直接返回数据';  //这里直接返回数据
})
.then(function(data){
    console.log(data);
});

运行结果:

异步任务1执行完成
随便什么数据1
异步任务2执行完成
随便什么数据2
直接返回数据      

promise里面的then函数仅仅是注册了后续需要执行的代码,真正的执行是在resolve方法里面执行的,理清了这层,再来分析源码会省力的多。

链接:

(ES6 Promise 用法)

Promise设计模式 (​​https://zhuanlan.zhihu.com/p/42377418​​ (30分钟,让你彻底明白Promise原理))

Promise的实现过程,其主要使用了设计模式中的观察者模式:

继续阅读