在
JavaScript
的世界中,全部代碼都是單線
執行的。 因爲這個「缺陷」,致使JavaScript
的全部網絡操做,瀏覽器事件,都必須是異步執行。異步執行能夠用:javascript
function call(id, callback){
return function(){
callback(id+1);
}
}
let fn = call(3, function(id){
console.log(id);
})
fn();
複製代碼
lodash 裏面的after函數實現方法html
function after(times, callback){
return function(){
//次數一直減小
if(--times == 0){
callback();
}
}
}
let fn = after(3, function(){
console.log('after 被調用了三次');
})
fn();
fn();
fn();
複製代碼
接下來就是常見的讀取數據的問題,回調函數的話,咱們只能一層一層往下讀取,很容易就進入了回調地獄
這個可怕的狀態java
let fs = require('fs');
let school = {}
fs.readFile('./age.txt', 'utf8', function (err, data) {
school['name'] = data;
fs.readFile('./name.txt', 'utf8', function (err, data) {
school['age'] = data;//{ name: 'cjw', age: '18' }
});
});
複製代碼
發佈者和訂閱者是沒有依賴關係的
你可能對發佈訂閱有點陌生,其實只要在DOM節點上面綁定過事件函數,那就使用過發佈—訂閱模式。node
document.body.addEventListener('click',function(){
alert(2);
},false);
document.body.click(); //模擬用戶點擊
複製代碼
實現原理
首先用一個數組arr保存回調函數,而後觸發emit的時候,arr裏面的回調函數一一執行es6
let fs = require('fs');
let dep = {
arr: [],//保存回調函數
on(callback){
this.arr.push(callback);
},
emit(){
this.arr.forEach(item=>{
item();
})
}
}
let school = {};
//這裏先加一個回調函數 (訂閱)
dep.on(function(){
if(Object.keys(school).length === 2){
console.log(school);//{ name: 'cjw', age: '18' }
}
})
//
fs.readFile('./age.txt', 'utf8', function(err, data){
school['name'] = data;
dep.emit();//發佈,調用dep.arr 裏面的回調函數一一執行
})
fs.readFile('./name.txt', 'utf8', function(err, data){
school['age'] = data;
dep.emit();//發佈
})
複製代碼
觀察者模式 發佈和訂閱的 被觀察者是依賴於觀察者的
web
//觀察者
class Observer{
constructor(){
this.arr = [];
this.val = 1;
}
updateVal(val){
this.val = val;
this.notify();
}
notify(){
this.arr.forEach(s=>s.update());
}
save(s){//保存一個對象
this.arr.push(s);
}
}
// 被觀察者,被觀察者有一個更新的方法。
class Subject{
update(){
console.log('update')
}
}
let s = new Subject();
let observer = new Observer();
observer.save(s);//保存一個對象
observer.save(s);
observer.updateVal(21);//更新值的時候,被觀察者也執行一個更新的方法
複製代碼
promise
有如下兩個特色:
1
.對象的狀態不受外界影響。Promise
對象表明一個異步操做,有三種狀態:pending
(進行中)、fulfilled
(已成功)和rejected
(已失敗)。只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是「承諾」,表示其餘手段沒法改變。
2
.一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。Promise
對象的狀態改變,只有兩種可能:從pending
變爲fulfilled
和從pending
變爲rejected
npm
let fs = require('fs');
function read(url){
return new Promise((resolve, reject)=>{
fs.readFile(url, 'utf8', (err, data)=>{
if(err) reject(err);
resolve(data);
})
})
}
let school = {};
read('./name.txt').then(data=>{
school['name'] = data;
return read('age.txt');
}).then(data=>{
school['age'] = data;
console.log(school);//{ name: 'cjw', age: '18' }
})
複製代碼
Promise.prototype.then()
Promise
實例具備then
方法,也就是說,then
方法是定義在原型對象編程
//let Promise = require('./promise.js');
let p = new Promise((resolve, reject)=>{
setTimeout(function(){
reject('成功');
},100)
reject('3');
})
p.then((value)=>{
console.log(value);//3,這裏是3由於,只能從一個狀態panding到另外一個狀態
}, (reason)=>{
console.log(reason);
})
複製代碼
基本概念
數組
1.new Promise
時須要傳遞一個executor
執行器,執行器會馬上執行
2.執行器中傳遞了兩個參數 resolve
成功的函數 他調用時能夠傳一個值 值能夠是任何值 reject
失敗的函數 他調用時能夠傳一個值 值能夠是任何值
3.只能從pending
態轉到成功或者失敗
4.promise
實例。每一個實例都有一個then
方法,這個方法傳遞兩個參數,一個是成功另外一個是失敗
5.若是調用then
時 發現已經成功了會讓成功函數執行而且把成功的內容看成參數傳遞到函數中
6.promise
中能夠同一個實例then
屢次,若是狀態是pengding
須要將函數存放起來 等待狀態肯定後 在依次將對應的函數執行 (發佈訂閱)
7.若是類執行時出現了異常 那就變成失敗態
promise
Promise.prototype.then()
的實現
function Promise(executor){
var self = this;
self.status = 'pending';//從pending 轉換爲resolved rejected
self.value = undefined;
self.reason = undefined;
self.onResolved = [];//專門存放成功的回調
self.onRejected = [];//專門存放失敗的回調
//pending -> resolved
function resolve(value){
if(self.status === 'pending'){
self.value = value;
self.status = 'resolved';
self.onResolved.forEach(fn=>fn());
}
}
//pending -> rejected
function reject(reason){
if(self.status === 'pending'){
self.reason = reason;
self.status = 'rejected';
self.onRejected.forEach(fn=>fn());
}
}
try{
executor(resolve, reject);
}catch(e){
reject(e);
}
}
//then方法的實現
Promise.prototype.then = function(onfulfilled, onrejected){
let self = this;
if(self.status === 'resolved'){//判斷狀態,resolved時候,返回value
onfulfilled(self.value);
}
if(self.status === 'rejected'){//判斷狀態,rejected時候,返回reason
onrejected(self.reason);
}
if(self.status === 'pending'){
self.onResolved.push(function(){
onfulfilled(self.value);
})
self.onRejected.push(function(){
onfulfilled(self.reason);
})
}
}
module.exports = Promise;
複製代碼
Promise.prototype.catch()
Promise.prototype.catch方法
是.then(null, rejection)
的別名,用於指定發生錯誤時的回調函數。
let p = new Promise((resolve, reject)=>{
resolve();
})
p.then(data=>{
throw new Error();
}).then(null).catch(err=>{
console.log('catch', err)
}).then(null, err=>{
console.log('err', err);
})
複製代碼
實現原理
Promise.prototype.catch = function(onrejected){
return this.then(null, onrejected);
}
複製代碼
Promise.all
Promise.all
方法用於將多個 Promise 實例,包裝成一個新的 Promise
實例。
const p = Promise.all([p1, p2, p3]);
複製代碼
實現原理
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let results = []; let i = 0;
function processData(index, data) {
results[index] = data; // let arr = [] arr[2] = 100
if (++i === promises.length) {
resolve(results);
}
}
for (let i = 0; i < promises.length; i++) {
let p = promises[i];
p.then((data) => { // 成功後把結果和當前索引 關聯起來
processData(i, data);
}, reject);
}
})
}
複製代碼
Promise.race
Promise.race
方法一樣是將多個 Promise
實例,包裝成一個新的 Promise
實例。
const p = Promise.race([p1, p2, p3]);
複製代碼
上面代碼中,只要p1
、p2
、p3
之中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的 Promise
實例的返回值,就傳遞給p的回調函數。
let Promise = require('./e2.promise');
let fs = require('mz/fs');
Promise.race([
fs.readFile('./age.txt', 'utf8'),
fs.readFile('./name.txt', 'utf8')
]).then(data=>{
console.log(data);
})
複製代碼
實現原理
Promise.race = function(promises){
return new Promise((resolve, reject)=>{
for(let i=0; i< promises.length; i++){
let p = promises[i];
p.then(resolve, reject);
}
})
}
複製代碼
Promise.resolve
Promise.resolve方法容許調用時不帶參數,直接返回一個resolved狀態的 Promise 對象。
const p = Promise.resolve();
p.then(function () {
// ...
});
複製代碼
實現原理
Promise.resolve = function(value){
return new Promise((resolve, reject)=>{
resolve(value);
})
}
複製代碼
Promise.reject
Promise.reject方法容許調用時不帶參數,直接返回一個rejected狀態的 Promise 對象。
const p = Promise.reject();
p.then(function () {
// ...
});
複製代碼
實現原理
Promise.reject = function(reason){
return new Promise((resolve, reject)=>{
reject(reason);
})
}
複製代碼
promise
的一些擴展庫async + await = generator + co
generator
生產迭代器的
生成器函數 * generator
通常配合 yield
function * read() {
yield 1;
yield 2;
yield 3;
return 100
}
let it = read();
console.dir(it.next());
console.dir(it.next());
console.dir(it.next());
console.dir(it.next());
//結果:
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: 100, done: true }
複製代碼
promise + generator
let fs = require('mz/fs');
// let co = require('co');
function * read(){
let age = yield fs.readFile('./age.txt', 'utf8');
return age;
}
//co 原理
function co(it){
return new Promise((resolve, reject)=>{
function next(data){
let { value, done } = it.next(data);
if(!done){
value.then(data=>{
next(data);
}, reject)
}else{
resolve(value);
}
}
next();
})
}
co(read()).then(data=>{
console.log(data);//18
}, err=>{
console.log(err);
})
複製代碼
async + await
是es7的語法
let fs = require('mz/fs');//這個mz庫將nodejs裏面的fs所有函數都promise化
// async 函數就是promise es7
// 回調的問題 不能try/catch 併發問題
async function read() {
let age = await fs.readFile('name.txt','utf8')
return age
}
read().then(data=>{
console.log(data);//cjw
})
複製代碼
測試代碼是否符合a+ 規範 爲了讓其能測試
npm install promises-aplus-tests -g
promises-aplus-tests 文件名 能夠測試
複製代碼
/*
* @Author: caijw
* @Date: 2018-10-01 15:04:43
* @Last Modified by: caijw
* @Last Modified time: 2018-10-08 22:41:06
*/
function Promise(executor){
var self = this;
self.status = 'pending';//從pending 轉換爲resolved rejected
self.value = undefined;
self.reason = undefined;
self.onResolved = [];//專門存放成功的回調
self.onRejected = [];//專門存放失敗的回調
//pending -> resolved
function resolve(value){
if(self.status === 'pending'){
self.value = value;
self.status = 'resolved';
self.onResolved.forEach(fn=>fn());
}
}
//pending -> rejected
function reject(reason){
if(self.status === 'pending'){
self.reason = reason;
self.status = 'rejected';
self.onRejected.forEach(fn=>fn());
}
}
try{
executor(resolve, reject);
}catch(e){
reject(e);
}
}
//這裏主要就是遞歸循環,判斷是否爲promise,若是是promise就繼續遞歸循環下去。
function resolvePromise(promise2, x, resolve, reject){
if(promise2 === x){
return reject(new TypeError('循環引用'));
}
let called;
if(x!=null && (typeof x === 'object' || typeof x === 'function')){
try{
let then = x.then;
//假設他是一個promise,then方法就是一個函數
if(typeof then === 'function'){
then.call(x, (y)=>{
if(called) return;
called = true;
// 遞歸解析 若是resolve的是一個promise 就要不停的讓resolve的結果進行處理
resolvePromise(promise2, y, resolve, reject);
},(e)=>{
if(called) return;
called = true;
reject(e);
})
}else{//不是就返回
resolve(x);
}
}catch(e){
if(called) return;
called = true;
reject(e);
}
}else{
resolve(x);
}
}
//至返回錯誤的 catch 就是不寫成功的回調的then方法
Promise.prototype.catch = function(onrejected){
return this.then(null, onrejected);
}
//1.解決輸出的順序的問題
// all方法的參數 是一個數組,會按照數組的結果放到成功的回調裏(只有全成功纔算成功)
// race方法參數也是一個數組。會同時發起併發,可是以返回最快的結果爲結果
Promise.race = function(promises){
return new Promise((resolve, reject)=>{
for(let i=0; i< promises.length; i++){
let p = promises[i];
p.then(resolve, reject);
}
})
}
Promise.reject = function(reason){
return new Promise((resolve, reject)=>{
reject(reason);
})
}
Promise.resolve = function(value){
return new Promise((resolve, reject)=>{
resolve(value);
})
}
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let results = []; let i = 0;
function processData(index, data) {
results[index] = data; // let arr = [] arr[2] = 100
if (++i === promises.length) {
resolve(results);
}
}
for (let i = 0; i < promises.length; i++) {
let p = promises[i];
p.then((data) => { // 成功後把結果和當前索引 關聯起來
processData(i, data);
}, reject);
}
})
}
//回調函數
Promise.prototype.then = function(onfulfilled, onrejected){
// onfulfilled / onrejected是一個可選的參數
onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : val=>val;
onrejected = typeof onrejected === 'function' ? onrejected :err => {
throw err;
}
let self = this;
let promise2;
promise2 = new Promise((resolve, reject)=>{
if(self.status === 'resolved'){
setTimeout(()=>{
try{
let x = onfulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
}, 0)
}
if(self.status === 'rejected'){
setTimeout(()=>{
try{
let x = onrejected(self.reason);
resolvePromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
}, 0)
}
if(self.status === 'pending'){
self.onResolved.push(function(){
setTimeout(()=>{
try{
let x = onfulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
}, 0)
})
self.onRejected.push(function(){
setTimeout(()=>{
try{
let x = onrejected(self.reason);
resolvePromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
}, 0)
})
}
})
return promise2;
}
// 語法糖 簡化問題 嵌套的問題 ,被廢棄了
Promise.defer = Promise.deferred = function(){
let dfd = {};
dfd.promise = new Promise((resolve, reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
module.exports = Promise;
複製代碼
終於擼完promise了,小夥伴們看了,感受有收穫,請點個贊