【ES6 笔记】Promise 与异步编程

2018-11-29  本文已影响0人  ___Jing___

在前面先说明一下,虽然经常会有人拿Promise和Ajax做比较,但是二者真的不是一个东西。Ajax用来获取后台的数据,Promise是ES6引入的,用来充当异步操作与回调函数之间的中介。

异步编程的背景知识

let button = document.getElementById( 'btn' );
button.onclick = function(event){
  console.log("Clicked");
};

事件模型适用于处理简单、低频的交互行为,对于更复杂的需求来说不是很灵活。

readFile('example.txt',function(err, contents){
  if(err) throw err;
  console.log("Finished");
});
console.log("Hello world!")

//输出结果如下(在不抛出错误的情况下)
Hello world!
Finished

回调模式比事件模型更灵活,但是需要添加错误处理机制,并且容易陷入回调地狱。

Promise的基础知识

Promise相当于异步操作结果的占位符,它不会去订阅一个事件,也不会传递一个回调函数给目标函数,而是让函数返回一个Promise。

// readFile 承诺将在未来的某个时刻完成
let promise = readFile('example.txt');

这段代码中,readFile不会立即开始读取文件,函数会先返回一个表示异步读取操作的Promise对象,未来对这个对象的操作完全取决于Promise的生命周期。

Promise的生命周期

每个 Promise 都会经历一个短暂的生命周期:先是处于进行中(pending)的状态,此时操作尚未完成,所以它也是未处理的(unsettled)的;一旦异步操作执行结束,Promise 则变为已处理(settled)的状态。
在之前的例子中,当readFile函数返回Promise时它变为pending状态,操作结束后,Promise可能会进入到以下两个状态中的其中一个:

//所有的Promise都有then方法,接受两个参数
let promise = readFile('example.txt');
promise.then(function(contents){
  // fulfilled
},function(error){
  // rejected
})
创建未完成的 Promise
// let定义的promise就是未完成的promise
let promise = new Promise(function(resolve, reject){
    console.log("Promise");
    resolve();
})
// promise.then 是处理程序
promise.then(function(){
    console.log("Resolved");
})
console.log("Hi")
// 输出结果
Promise
Hi
Resolved

完成处理程序和拒绝处理程序总是在执行器完成后被添加到任务队列的末尾。

创建已处理的 Promise
// let 定义的promise就是完成的promise
let promise = Promise.resolve(100);
// promise.then是处理程序
promise.then(function(value){
  console.log(value);//100
})

由于该promise永远不会存在拒绝状态,因为该promise的拒绝处理程序永远不会被调用。

// let 定义的promise就是完成的promise
let promise = Promise.reject(222);
// promise.catch是处理程序
promise.catch(function(value){
  console.log(value);//222
})

任务附加到这个Promise的拒绝程序都将被调用,但却不会调用完成处理程序。

Note: 如果像Promise.resolve()方法或是Promise.reject()方法传入一个Promise,那么这个Promise会被直接返回。

let thenable = {
    then : function(resolve,reject){
        resolve(100)
    }
};

let p = Promise.resolve( thenable );
p.then(function(value){
    console.log(value);// 100
})
执行器错误

如果执行器内部抛出一个错误,则Promise的拒绝处理程序就会被调用。

let promise = new Promise(function(resolve,reject){
    throw new Error('Explosion')
})

promise.catch(function(err){
    console.log(err.message); //Explosion
})

全局的 Promise 拒绝处理

有关Promise的其中一个最具争议的问题是,如果在没有拒绝处理程序的情况下拒绝一个Promise,那么不会提示失败信息,这是Javascript语言中唯一一处没有强制报错的地方。
Promise的特性决定了很难检测一个Promise是否被处理过。

Node.js 环境的拒绝处理

在Node.js中,处理Promise拒绝时会触发process对象上的两个事件:


let rejected;

process.on('unhandledRejection',function(error, promise){
    console.log(error.message); // Explosion
    console.log(rejected == promise); // true
})

rejected = Promise.reject(new Error('Explosion'));


let rejected;

process.on('rejectionHandled',function( promise){
    console.log(rejected == promise); // true
})

rejected = Promise.reject(new Error('Explosion'));

// 等待添加拒绝处理程序
setTimeout(function(){
    rejected.catch(function(error){
        console.log(error.message);  // Explosion
    })
},1000)

这里的 rejectionHandled 事件在拒绝处理程序最后被调用时触发,如果在创建rejected 之后直接添加拒绝处理程序,那么rejectionHandled事件不会被触发,因为rejected创建的过程与拒绝处理程序的调用在同一个事件循环中,此时rejectionHandled事件尚未生效。

浏览器环境的拒绝处理程序

浏览器也是通过触发两个事件来识别未处理的拒绝的,虽然这些事件是在window对象上触发的,但实际上与Node.js中的完全等效。

在浏览器中,事件处理程序接受一个有以下属性的事件对象作为参数:

let rejected

window.onunhandledrejection = function(event){
    console.log(event.type);    // 'unhandledrejection'
    console.log(event.reason.message);  // 'Explosion'
    console.log(rejection === event.promise );  // true
};

window.onrejectionhandled = function(event){
    console.log(event.type);  //'rejectionhandled'
    console.log(event.reason.message);  //'Explosion'
    console.log(rejected === event.promise);  // true
}

rejected = Promise.reject(new Error('Explosion'))

串联 Promise

let p1 = new Promise(function(resolve, reject){
    resolve(666)
});

p1.then(function(value){
    throw new Error('Boom')
}).catch(function(error){
    console.log(error.message);   // Boom
})
let p1 = new Promise(function(resolve, reject){
    resolve(666)
})

p1.then(function(value){
    console.log(value)  // 666
    return value+1
}).then(function(value){
    console.log(value)  //667
})

拒绝处理程序中也可以做相同的事情,在必要时,即使其中一个Promise失败也能恢复整条链的执行。

let p1 = new Promise(function(resolve, reject){
    resolve(1)
})

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

p1.then(function(value){
    //第一个完成处理程序
    console.log(value)  //1
    return p2
}).then(function(value){
    // 第二个完成处理程序
    console.log(value)  //2
})

响应多个Promise

let p1 = new Promise(function(resolve, reject){
    resolve(1)
})
let p2 = new Promise(function(resolve, reject){
    resolve(2)
})
let p3 = new Promise(function(resolve, reject){
    resolve(3)
})
let p4 = Promise.all([p1,p2,p3])
p4.then(function(value){
    console.log(Array.isArray(value));   //true
    console.log(value);   // [1,2,3]
})

所有传入Promise.all()方法的Promise只要有一个被拒绝,那么返回的Promise没等所有的Promise都完成就立即被拒绝。

let p1 = Promise.resolve(1)
let p2 = new Promise(function(resolve, reject){
    resolve(2)
})
let p3 = new Promise(function(resolve, reject){
    resolve(3)
})
let p4 = Promise.race([p1,p2,p3])
p4.then(function(value){
    console.log(value);   // 1
})

实际上,传给Promise.race()方法的Promise会进行竞选,以决出哪一个先被解决,如果先解决的是已完成Promise,则返回已完成Promise;如果先解决的是已拒绝Promise,则返回已拒绝Promise。

自Promise继承

Promise与其他内建类型一样,也可以作为基类派生其他类。

class MyPromsie extends Promise {
    // 使用默认的构造函数
    success(resolve, reject){
        return this.then(resolve, reject)
    }
    failure(reject){
        return this.catch(reject)
    }
}

let promise = new MyPromise(function(resolve, reject){
    resolve(1)
})

promise.success(function(value){
    console.log(value)   // 1
}).failure(function(value){
   console.log(value)   
})

基于Promise的异步任务执行

... 持续更新中 ...

上一篇 下一篇

猜你喜欢

热点阅读