Promise

Promise由浅入深

前言

我们都知道javaScript是单线程的,代码执行的顺序是从上往下。但是在实际开发中难免会出现异步编程。所谓异步就是代码执行到这里不会立即执行得出结果,比如ajax请求,文件的读取等,为了不阻塞进程,先把它放到事件队列中。

但是传统的ajax的请求方式有一个很大问题,比如有多个请求并且下次的请求需要依赖上次的请求结果,那么就会出现多层嵌套,也就是所谓的“回调地狱”。

es6中提出了Promise。可以作为解决异步的一种方式。promise可以理解成是一个对象的代理,通常用来代理一个未来的值,为什么说是未来呢,因为这个值通常是由异步操作得出的,不像同步操作那样立即就可以拿到值。
我们可以把Promise比做一个容器,它里边包含着未来才会结束的事件的结果。

Promise有三种状态:

  • pending 初始状态
  • fulfilled 成功完成
  • rejected 失败

1、 声明未执行的Promise实例是pending状态,执行成功调用resolve方法是fulfilled状态,执行失败调用rejecte方法是rejected状态。

2、 Promise状态一旦改变,就不会在变了。也就是说状态的改变只有两种可能:(1)从pending变成fulfilled (2)从pending变成rejected。只要状态一旦改变,状态就凝固了,不会在发生改变。

Promise参数

Promise接受一个函数参数,函数参数又有两个参数,分别是resolve和rejecte。在函数参数中执行相应的异步操作,当执行成功,调用resolve,执行失败调用rejecte。通过reslove和rejecte传递结果。

Promise的前因后果

new Promise

var promise = new Promise(function (resolve, reject) {
    console.log('create promise')
    setTimeout(function () {
        resolve('first value');
        console.log('最后执行');
    }, 1000);
        
})

// 实例创建完成,函数内部有异步操作,不会立即执行resolve方法,所以状态为pending
console.log(promise)

输出先后顺序:

"create promise"
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
"最后执行"

从输出的先后顺序可以看出来Promise的创建以及函数参数的执行是一个同步的过程,实例创建完成后由于函数参数中有一个异步操作,不会立即执行resolve方法,所以Promise的实例仍是pending状态,。

Promise实例构造函数的原型上有then方法和catch方法,可以被其实例所共享。其中then方法接受两个函数参数,第一个函数的参数就是resolve中接收的值,第二个函数是错误处理;catch方法是rejecte中的值。

Promise实例调用then方法的实现思路:

// 伪代码
Promise.prototype.then = function(success, faile){
    // then方法的伪代码 执行相应的函数参数
    // 如果异步操作成功执行success方法
    success();
    // 反之
    faile();
    // 最后返回一个新的Promise对象
    return newPromise;
}

链式调用

因为then方法的两个函数参数默认会返回一个新的Promise对象,所以可以进行链式调用。调用规则:

  • 当没有显式return时,默认返回新的Promise对象;
  • 当return的不是Promise对象时,那么return的值会作为then返回的新Promise实例的resolved参数;
  • 当return的是一个Promise实例时,那么then返回的Promise实例就是该实例;

具体如下:

promise.then(function (response) {
    console.log(response); // first value
    console.log(promise); // Promise对象 状态已经由pending 变为 resolved
}).then(function (response) {
    console.log(response); // undefined 
    // 从这里可以看出来,then()方法返回的是一个新的promise,因为新的Promise实例中并没有执行resolve()方法,更没有什么异步操作,所以这里的值是undefined(声明未初始化)

    // 手动创建并返回一个新的Promise实例 
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve('second value')
        }, 1000);
    })
}).then(function(response){
    console.log(response); // second value
    // 这里之所以能够获取到值,是因为上一个then()返回的Promise成功执行异步操作

    return '还可以返回非Promise实例'
}).then(function(response){
    console.log(response) // 还可以返回非Promise实例
})

下面通过简单的示例一步步深入了解。

一、Promise立即执行

var promise = new Promise(function(resolved, rejected){
    console.log('创建promise')
    resolved('成功');
    rejected('失败');
})

console.log('执行完成')

promise.then(function(res){
    console.log(res);
}).catch(function(err){
    console.log(err);
})

输出结果:

创建Promise
执行完成
成功

解释:Promise对象表示将来某一时刻要发生的事件,但是创建(new)Promise是一个同步的过程,包括传入的函数参数也是一个立即执行的函数,只是函数内部是异步操作还是同步操作就不确定了。所以以上代码是按顺序输出。

二、Promise的三种状态

var p1 = new Promise(function(resolve,reject){
  resolve(1);
});
var p2 = new Promise(function(resolve,reject){
  setTimeout(function(){
    resolve(2);  
  }, 500);      
});
var p3 = new Promise(function(resolve,reject){
  setTimeout(function(){
    reject(3);  
  }, 500);      
});

p1、p2、p3的状态
console.log(p1);  // resolved
console.log(p2);  // pending
console.log(p3);  // pending

setTimeout(function(){
  console.log(p2); // resolved
}, 1000);
setTimeout(function(){
  console.log(p3); // rejected
}, 1000);


p1.then(function(value){
  console.log(value); // 1
});
p2.then(function(value){
  console.log(value); // 2
});
p3.catch(function(err){
  console.log(err);   // 3
});

输出结果:

Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 1}
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
1
2
3
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 2}
Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: 3}

解释:其实我们可以把Promise内部理解成是一个状态机,分别有pending resolved rejected三种状态。 当刚创建Promise时处于pending状态,所以p2,p3的状态为pending,因为p1的函数参数执行的是同步代码,Promise刚创建完成resolved也就执行了,所以p1是resolved状态。然后又是两个setTimeout函数,所以会先分别执行对应的then方法,输出 1、 2、 3;1s后执行setTimeout函数,此时p2 p3的状态分别已经变成了resolved和rejected。

三、Promise状态不可逆

var p1 = new Promise(function(resolve, reject){
  resolve("success1");
  resolve("success2");
});

var p2 = new Promise(function(resolve, reject){
  resolve("success");
  reject("reject");
});

p1.then(function(value){
  console.log(value);
});

p2.then(function(value){
  console.log(value);
});

输出结果:

'success1'
'success'

解释:Promise状态一旦从pending变成resolved或rejected时状态就凝固了。不管再调用resolved或rejected都不管用。

四、链式调用

    var p = new Promise(function (resolve, reject) {
        resolve(1);
    });
    
    p.then(function (value) {         
        console.log(value);  // 1
        return value * 2;
    }).then(function (value) {     
        console.log(value);  // 2
    }).then(function (value) {       
        console.log(value);  // undefined
        return Promise.resolve('resolve');
    }).then(function (value) {               
        console.log(value);  // resolve
        return Promise.reject('reject');
    }).then(function (value) {               
        console.log('resolve: ' + value);
    }).catch(function(err){
        console.log("rejected: ", err) // reject
    }) 

输出结果:

1
2
"undefined"
"resolved"
"rejected: rejected"

解释: Promise对象的then方法返回一个新的Promise对象,因此可以链式调用。第一个then有返回值,所以在二个then中输出2;而第二个then没有返回值,所以第三个then输出undefined;第三个then返回一个Promise的resolved方法,所以第四个then中输出resolved;但是第四个then中返回Promise的rejected方法,所以第五个then的第一个函数参数不会输出,而是在第二个函数参数或者catch中输出rejected。

五、Promise then()回调的异步性

    var promise = new Promise(function(resolved, rejected){
        resolved('success')
        console.log('first')
    })

    promise.then(function(res){
        console.log(res)
    })

    console.log('second')

输出结果:

'first'
'second'
'success'

解释:new Promise、函数参数以及其内部的代码是一个同步的过程,所以会先输出 first,但是then方法中的回调函数是异步的,所以先输出 second,最后输出 success。

六、Promise中的异常

var p1 = new Promise( function(resolve,reject){
  foo.bar();
  resolve( 1 );      
});
console.log(p1) 
// Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: ReferenceError: foo is not defined}

p1.then(
  function(value){
    console.log('p1 then1 value: ' + value);
  },
  function(err){
    console.log('p1 then1 err: ' + err);// 报错 foo is not defined
    // 没有显示返回
  }
).catch(function(err){
    console.log('err', err)
}).then(
  function(value){
    console.log('p1 then2  value: '+value); // p1 then2 value: undefined 
    
  },
  function(err){
    console.log('p1 then2  err: ' + err);
  }
);

var p2 = new Promise(function(resolve,reject){
  resolve( 2 );    
});

p2.then(
  function(value){
    console.log('p2 then1 value: ' + value);// p2 then1 value: 2
    foo.bar(); // 报错
  }, 
  function(err){
    console.log('p2 then1 err: ' + err);
  }
).then(
  function(value){
    console.log('p2 then2 value: ' + value);
  },
  function(err){
    console.log('p2 then2 err: ' + err); // p2 then2 err: foo is not defined
    return 1;
  }
).then(
  function(value){
    console.log('p2 then3 value: ' + value);// p2 then3 value: 1
  },
  function(err){
    console.log('p2 then then then err: ' + err);
  }
);

输出结果:

p1 then1 err: ReferenceError: foo is not defined
p2 then1 value: 2
p2 then2 err: ReferenceError: foo is not defined
p1 then2  value: undefined
p2 then3 value: 1

解释:Promise中的异常由then参数中第二个回调函数(Promise执行失败的回调)处理,异常信息将作为Promise的值。异常一旦得到处理,then返回的后续Promise对象将恢复正常,并会被Promise执行成功的回调函数处理。另外,p1、p2 多个then的回调是交替执行的 ,这正是由Promise then回调的异步性决定的。

七、Promise.resolve()

Promise.resolve(value)返回给定给定值解析后的Promise对象。

value分三种情况:

  1. 普通值 --- 以该值作为resolved状态返回promise
Promise.resolve("Success").then(function(value) {
  console.log(value); // "Success"
}, function(value) {
  // 不会被调用
});
  1. Promise对象 --- 返回值即为该传入的Promise对象
let p1 = Promise.resolve('success');
let p2 = Promise.resolve(p1);
cast.then(value => {
  console.log('value: ' + value);
});
console.log('p1 === p2 ? ' + (p1 === p2));

/*
*  打印顺序如下,这里有一个同步异步先后执行的区别
*  p1 === p2 ? true
*  value: success
*/
  1. 带有then方法的对象 --- 返回的promise会“跟随”这个thenable的对象,采用它的最终状态(指resolved/rejected/pending/settled)
// Resolve一个thenable对象
var p1 = Promise.resolve({ 
    then(onFulfill, onReject) { 
        onFulfill("fulfilled!");
    }
});
console.log(p1 instanceof Promise) // true, 这是一个Promise对象

p1
    .then(res => {
        console.log(res); // "fulfilled!"
    })
    .catch(err => {
        console.log(err)
    })

// Thenable在callback之前抛出异常
let thenable = { 
    then(resolve) {
        throw new Error("Throwing");
        resolve("Resolving")
    }
};

let p2 = Promise.resolve(thenable);

p2
    .then(res => {
        console.log(res)
    })
    .catch(err => {
        console.log(err)  // Throwing
    })

// Thenable在callback之后抛出异常
let thenable = { 
    then(resolve) {
        resolve("Resolving");
        throw new Error("Throwing");
    }
}

let p3 = Promise.resolve(thenable);
p3  
    .then(res => {
        console.log(res) // Resolving
    })
    .catch(err => {
        console.log(err)
    })

    let thenable = { 
    then(resolve) {
        resolve("Resolving");
        throw new Error("Throwing");
    }
}


let thenable = { 
    then(resolve, reject) {
        reject("Oops")
    }
}

let p4 = Promise.resolve(thenable);
p4  
    .then(res => {
        console.log(res) 
    })
    .catch(err => {
        console.log(err) // oops
    })
  1. resolve异步解析Promise对象
let p1 = Promise.resolve( 1 );
let p2 = Promise.resolve( p1 );
let p3 = new Promise(function(resolve, reject){
  resolve(1);
});
let p4 = new Promise(function(resolve, reject){
  resolve(p1);
});

console.log(p1 === p2); // true
console.log(p1 === p3); // false
console.log(p1 === p4); // false
console.log(p3 === p4); // false

p4.then(function(value){
  console.log('p4=' + value);
});

p2.then(function(value){
  console.log('p2=' + value);
})

p1.then(function(value){
  console.log('p1=' + value);
})

输出结果:

true
false
false
false

p2=1
p1=1
p4=1

解释:Promise.resolve()中的参数可以是一个普通值或者一个Promise对象,如果是普通值,则它返回一个Promise对象,PromiseValue就是该值;如果参数是Promise对象,那么直接返回这个Promise对象参数。所以p1==p2。但是因为通过new的方式创建的Promise对象都是一个新的对象,所以其余都是false。

为什么p4的then最先调用,但在控制台上是最后输出结果的呢?因为p4的resolve中接收的参数是一个Promise对象p1,resolve会对p1”提取“,获取p1的状态和值,但这个过程是异步的。也就是说Promise中的resolve方法会对接收的Promise对象进行“分解”。

八、resolve vs reject

var p1 = new Promise(function(resolve, reject){
  resolve(Promise.resolve('resolve'));
});

var p2 = new Promise(function(resolve, reject){
  resolve(Promise.reject('reject'));// 拆箱获取值 reject
});

var p3 = new Promise(function(resolve, reject){
  reject(Promise.resolve('resolve'));
});



p1.then(
  function (value){
    console.log('p1', value); // p1 resolve
    console.log('p1 fulfilled: ' + value); //p1 fulfilled resolve
  }, 
  function (err){
    console.log('p1 rejected: ' + err);
  }
);

p2.then(
  function (value){
    console.log('p2', value)
    console.log('p2 fulfilled: ' + value); 
  }, 
  function (err){
      console.log('p2 err', err) // p2 err reject
    console.log('p2 rejected: ' + err); //p2  fulfilled reject
  }
);

p3.then(
  function (value){
      console.log('p3', value)
    console.log('p3 fulfilled: ' + value);
  }, 
  function (err){
    console.log(err) // Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "resolve"}
    console.log('p3 rejected: ' + err); // rejected [object Promise]
  }
);


setTimeout(function() {
    console.log(p1)
    console.log(p2)
    console.log(p3)
}, 100);

输出结果:

Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "resolve"}
p3 rejected: [object Promise]
p1 resolve
p1 fulfilled: resolve
p2 err reject
p2 rejected: reject

Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "resolve"}
{[[PromiseStatus]]: "rejected", [[PromiseValue]]: "reject"}
{[[PromiseStatus]]: "rejected", [[PromiseValue]]: Promise}

解释:
Promise回调中的第一个参数resolve,会对Promise执行"提取"。即当resolve的参数是一个Promise对象时,resolve会"提取"获取这个Promise对象的状态和值,(原封不动的返回传入的promise对象)但这个过程是异步的。

p1"提取"后,获取到Promise对象的状态是resolved,因此p1.then()中的第一个回调被执行;p2"提取"后,获取到Promise对象的状态是rejected,因此p2.then()中的第二个回调被执行。

但Promise回调函数中的第二个参数reject不具备”提取“的能力,reject的参数会直接传递给then方法中的第二个回调。因此,即使p3 reject接收了一个resolved状态的Promise,then方法中被调用的依然是rejected,并且参数就是reject接收到的Promise对象。

参考: MDN
阮一峰 Promise

你可能感兴趣的:(Promise)