异步和性能
文章目录

异步和性能

That seems like a crazy claim, right? In fact, it’s quite true. The JS engine itself has never done anything more than execute a single chunk of your program at any given moment[^1]

JS自身并没有实现各种意义上的异步,这些异步都是通过外部环境实现的。AJAX也是基于外部环境才得以实现,AJAX会首先提供一个Callback,然后浏览器先将Callback是实现放到Event Loop里面,然后在获取到Response的时候调用这个Callback。

Promise

关于传统Callback可能存在几个问题:

  1. 调用太早或者太晚

  2. 被莫名调用多次

  3. 可能会因为被调用的代码存在错误而中断callback,甚至无法返回错误信息

    Promise是为提供更高效的callback解决方案而发明

基本用法

Promise对象是一个构造函数,用来生成Promise实例。

new Promise()新建了一个Promise对象,然后promise.then()给这个对象创建回调并且返回另一个Promise对象,因为返回的又是一个Promise,可以继续执行promise.then()

1
2
3
4
5
6
7
8
9
10
11
12
var promise = new Promise(function(resolve, reject) {
// ... some code cost time

if (/* 异步操作成功 */){
//这里的参数value是传到下方promise.then()的第一个参数的参数
//实际上这一句就是在调用下方promise.then()的第一个参数所指向的函数
resolve(value);
} else {
//同理这一句是在调用下方promise.then()的第二个参数所指向的函数
reject(error);
}
});

随后可以使用then方法传递两个函数进行回调,注意我们给then()方法传递了两个参数,并且返回的是另一个Promise

1
2
3
4
5
promise.then(function(value) {
// success
}, function(error) {
// failure
});

一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});

promise.then(function() {
console.log('Resolved.');
});

console.log('Hi!');

// Promise
// Hi!
// Resolved

Promise的特性

  1. Promise的执行无法被阻止并且不受外界影响,Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

    由此即使出现JS错误也会将执行错误的结果传递回来,但是必须要通过reject()进行捕捉

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var p = new Promise( function(resolve,reject){
    foo.bar(); // `foo` is not defined!!!!!!!!!!!!!!!! ERROR!!!!!!!!!!!!!!!!!!!
    resolve( 42 ); // never gets here :(
    } );

    p.then(
    function fulfilled(){
    // never gets here :(
    },
    function rejected(err){
    // `err` will be a `TypeError` exception object
    // from the `foo.bar()` line.
    }
    );

    当然, 如果fulfilled里面代码报错了,就不会继续执行下面的部分,就如同自然JS跑出错误一般

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var p = new Promise( function(resolve,reject){
    resolve( 42 );
    } );

    p.then(
    function fulfilled(msg){
    foo.bar(); // `foo` is not defined!!!!!!!!!!!!!!!! ERROR!!!!!!!!!!!!!!!!!!!
    console.log( msg ); // never gets here :(
    },
    function rejected(err){
    // never gets here either :(
    }
    );
  2. 状态一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected

    同时这就意味着resolved()里面如果出现问题也不会中途跳转过去执行rejected(),而是会直接报JS错误。

    这一点和try-catch不一样

调用链

因为then返回的是另一个Promise,因此我们可以采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。

1
2
3
4
5
6
7
8
9
10
 promise = new Promise(function(resolve, reject) {
console.log('1');
resolve();
})
.then(function(){console.log(2)})
.then(function(){console.log(3)})

//1
//2
//3

实际上

1
2
3
4
5
6
7
8
9
getJSON("/post/1.json")
.then(function(post) {
return getJSON(post.commentURL);
})
.then(function funcA(comments) {
console.log("Resolved: ", comments);
}, function funcB(err){
console.log("Rejected: ", err);
});

以及更加复杂的调用链:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
p.then( function(){
p.then( function(){
p.then( function(){
console.log( "E" );
} );
console.log( "C" );
} );
console.log( "A" );
} );
p.then( function(){
p.then( function(){
console.log( "D" );
} );
console.log( "B" );
} );

其实很好理解,p已经完成了就输出AB
然后两段代码注册两个新的并已经完成,输出CD
然后前半段代码注册一个新的并已经完成,输出E

参考文献

http://es6.ruanyifeng.com/#docs/promise