547 Promise:Ajax 的串行、并行, Promise的executor和状态,then、catch、finally,then链

Promise

ES6 语法规范中新增加的内置类,用来处理 JS 中异步编程的,而我们所谓的 promise 设计模式,就是基于 promise 对异步操作进行管理。

对Ajax中的串行、并行导致的回调地狱、其他乱七八糟的事,进行管控。

我的理解:执行Promise的执行器函数executor过程中,如果执行了resolve函数,表示异步操作成功,把PromiseStatus改为fulfilled / resolved,Promise实例的then中执行resolved对应的方法;如果执行了reject函数,表示异步操作失败,把PromiseStatus改为rejected,Promise实例的then中执行rejected对应的方法。

在执行异步操作,等待的过程中,调用 实例.then(),往事件池【不是事件池】中添加 异步成功、失败对应的方法。【 .then()时,还不会执行.then()中的方法,只是把方法加进去。】

异步操作结束后,根据PromiseStatus,执行.then()中对应的方法。

实例.then():有2个方法,到底执行哪个方法,由当前实例的状态、value决定【value无关】。如果是executor函数,那就看执行resolve 还是 reject;如果不是executor函数,是then()中的函数,就看函数执行有没有报错,不管执行的是then()中的第几个函数。


异步编程中的“回调地狱”,Ajax 的串行、并行

Ajax 的串行

Ajax串行:只有第一个请求成功才能执行第二个,第二个成功才能执行第三个....最后一个请求成功后拿到了每一次请求的所有数据

$.Ajax({
  url: '/baseInfo',
  method: 'GET',
  data: {
    name: 'zhanglu'
  },
  success: result => {
    let scoreId = result.scoreId;

    $.Ajax({
      url: '/scoreInfo',
      method: 'GET',
      data: {
        id: scoreId
      },
      success: result => {
        let chinese = result.chinese;

        $.Ajax({
          url: '/paiming',
          method: 'GET',
          data: {
            num: chinese
          },
          success: result => { }
        });
      }
    });
  }
});

Ajax 的并行

Ajax 的并行:请求可以同时发送,但是需要等到所有请求都成功才会做一件事

// 三个请求可以同时发送,但是需要等到所有请求都成功才会做一件事
let chi = 100,
  eng = 12,
  math = 98;
let chiPai,
  engPai,
  mathPai;
let count = 0;

function func() {
  // func虽然被调用3次,但是count >= 3,才会执行if中的代码
  if (count >= 3) {
    // 处理自己要做的事情
  }
}

$.Ajax({
  url: '/pai?chi=' + chi,
  success: result => {
    chiPai = result;
    count++;
    func();
  }
});

$.Ajax({
  url: '/pai?eng=' + eng,
  success: result => {
    engPai = result;
    count++;
    func();
  }
});

$.Ajax({
  url: '/pai?math=' + math,
  success: result => {
    mathPai = result;
    count++;
    func();
  }
});

let p1 = new Promise((resolve, reject) => {
    console.log(111)
})
console.log(p1)

let p1 = new Promise((resolve, reject) => {
    console.log(111)
    resolve()
})
console.log(p1)


Promise基础语法

executor和状态

let PromiseExamp = new Promise(); // => Uncaught TypeError: Promise resolver undefined is not a function


// --------------------------------------


/*
 * new Promise([executor]): 第一个执行函数必须传递
 *   [executor 简称exe]
 *     1.exe是Promise类的一个回调函数,exe函数中放的就是当前要处理的异步操作,Promise内部会把它执行。
 *       new Promise的时候就会把exe执行,创建Promise的一个实例。
 *     2.Promise不仅把exe执行,而且还给exe传递两个参数(两个参数也是函数类型):
 * 		 => resolve函数:它执行代表Promise处理的异步操作是成功的,把Promise的状态改为fulfilled。
 *       => reject函数:它执行代表Promise处理的异步操作是失败的,把Promise的状态改为rejected。
 */


let PromiseExamp = new Promise((resolve, reject) => {
  // => 这里一般存放的都是即将要处理的异步任务,任务成功,执行resolve;任务失败,执行reject。当然,写同步的也可以。
  // 【executor不报错,还需要在executor中手动调用resolve,才会执行Promise实例的then函数中的完成处理函数;executor报错,不需要在executor中手动调用reject,就会执行Promise实例的then函数中的拒绝处理函数。】
  let ran = Math.random();
  setTimeout(() => {
    if (ran < 0.5) {
      reject(ran);
      return;
    }
    resolve(ran);
  }, 1000);
});

PromiseExamp.then(result => {
  // => 状态为fulfilled成功后执行(result:[[PromiseValue]])
  console.log('成功: ' + result);
}, error => {
  // => 状态为rejected失败后执行
  console.log('失败: ' + error);
}); 


js中的异常处理

// js中的异常处理
console.log(a); // => Uncaught ReferenceError: a is not defined
let b = 10;
console.log(b);

// => 在JS中当前行代码报错,会中断主线程的渲染(下面代码将不再执行)
// throw new Error(''):手动抛出一个异常错误,目的就是让后面代码不再执行
// 如果上面代码报错,不想让期影响后面的代码,我们需要做异常捕获:try catch finally

try {
  console.log(a);
} catch (e) {
  // => 错误信息
  console.log(e.message); // a is not defined
}

try {
  throw new Error('嘻嘻嘻')
} catch (e) {
  console.log(e.message); // 嘻嘻嘻
}

try {
  throw '哈哈哈'
} catch (e) {
  console.log(e); // 哈哈哈
}

let b = 10;
console.log(b);

then、catch、finally

/*
 * Promise.prototype
 *    then: 设置成功或者失败后执行的方法(成功或者失败都可以设置,也可以只设置一个)
 *       pro.then([success], [error])
 * 		 pro.then([success])
 *       pro.then(null, [error])
 *    catch: 设置失败后执行的方法
 *    finally: 设置不论成功还是失败都会执行的方法(一般不用)
 */

let PromiseExamp = new Promise((resolve, reject) => {
  setTimeout(() => {
    let ran = Math.random();
    ran < 0.5 ? reject(ran) : resolve(ran);
  }, 100);
});
PromiseExamp.then(result => {
  console.log('成功: ' + result);
});
PromiseExamp.catch(error => {
  console.log('失败: ' + error);
});
PromiseExamp.finally(x => {
  console.log('哈哈');
});


// 使用链式写法,则不会像上面那样:ran < 0.5 时,控制台报错
let PromiseExamp = new Promise((resolve, reject) => {
  setTimeout(() => {
    let ran = Math.random();
    ran < 0.5 ? reject(ran) : resolve(ran);
  }, 100);
});

PromiseExamp.then(result => {
  console.log('成功: ' + result);
}).catch(error => {
  console.log('失败: ' + error);
}).finally(x => {
  console.log('哈哈');
});
let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    let ran = Math.random();
    ran < 0.5 ? reject(ran) : resolve(ran);
  }, 100);
})

p1.then(result => console.log('成功: ' + result));
p1.catch(error => console.log('失败: ' + error));
p1.finally(() => console.log('嘻嘻嘻'));


console.log('-----------------------------')


let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    let ran = Math.random();
    ran < 0.5 ? reject(ran) : resolve(ran);
  }, 100);
});

p2.then(result => console.log('成功: ' + result))
  .catch(error => console.log('失败: ' + error))
  .finally(() => console.log('哈哈哈'))

then链

let pro1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    let ran = Math.random();
    console.log(ran)
    ran < 0.5 ? reject(ran) : resolve(ran);
  }, 100);
});

// 刚开始,pro1【.then】的状态是pedding。当pro1.then中2个方法中的任意一个执行,报错、成功、失败,只要执行的结果没报错,说明是成功的;可能这里又会返回一个异步操作的结果。2个方法中的任意一个执行,只要成功了,都会把pro3的 完成处理函数 执行;反之,则执行拒绝处理函数。第二次then监听的是 这个then的2个回调函数的执行结果
let pro2 = pro1.then(result => {
  console.log(`PRO1: SUCCESS`);
}, error => {
  console.log(`PRO1: ERROR`)
});

// 当pro2的状态为fulfilled 或 rejected时,执行pro2.then中的某个方法
let pro3 = pro2.then(result => {
  console.log(`PRO2: SUCCESS`);
}, error => {
  console.log(`PRO2: ERROR`)
});



// ---------------------------------------


// => then(onResolve,onReject)
// 执行then、catch、finally返回的结果是一个全新的Promise实例,所以可以链式写下去;下一个then中哪个方法会被执行,由上一个then中某个方法执行的结果来决定
// 上一个then中某个方法的返回值会传递给下一个then的某个方法中
// 实例.then():有2个方法,到底执行哪个方法,由当前实例的状态、value决定【alue无关】。如果是executor函数,那就看执行resolve 还是 reject;如果不是executor函数,是then()中的函数,就看函数执行有没有报错,不管执行的是then()中的第几个函数
new Promise((resolve, reject) => {
  // resolve(100); // => 把第一个Promise实例的value值改为100/-100
  reject(-100);
}).then(result => {
  console.log(result);
  // then中RETURN的结果相当于把当前这个新的Promise实例中的value值改为返回值
  return result * 10;
}, err => {
  console.log(err);
  return err / 10;
}).then(A => {
  console.log('A:' + A);
}, B => {
  console.log('B:' + B);
}).then(C => {

}, D => {

});


// ------------------------


// 如果当前Promise实例的状态确定后,都会到对应的then中找方法,如果then中没有对应的这个方法,则会向下顺延
// then(onfulfilled)、 then(null,onrejected)  
new Promise((resolve, reject) => {
  reject(-100);
}).then(A => {
  console.log(A);
  return A * 10;
}).catch(B => {
  console.log(B); // => -100
  return B * 10;
});



// --------------------------------


// 第一个.then()返回一个新的Promise实例
new Promise((resolve, reject) => {
  resolve(100);
}).then(A => {
  // => 执行报错,让.then创建的Promise实例变为失败状态,并且把报错的原因作为此Promise的value值 
  // 【而不是把return 的 A * 10作为返回值,其实这行代码报错,这个then中下面的代码就不会执行了。】
  console.log(AAAAA);
  return A * 10;
}).catch(B => {
  console.log(B); // => ReferenceError: AAAAA is not defined
  return '@';
}).then(C => {
  console.log(C); // => '@'
});


// --------------------------------


// (1)第一个Promise成功,找第1个then中的第一个函数,没有,不会报错,顺延到下一个成功;(2)第一个catch不代表成功,跳过;(3)找到第2个then中的第一个函数,执行,没报错,成功;(4)第3个then中的第一个函数,执行,没报错,成功;(5)第2个catch不执行;(6)第3个then是成功,第2个catch不执行,所以执行第4个then,报错;(7)执行第3个catch,没东西,顺延;(8)执行最后一个then的拒绝处理函数。
new Promise((resolve, reject) => {
  resolve();
}).then().catch(x => {
  console.log(1);
}).then(x => {
  console.log(2); // => OK
}).then(x => {
  console.log(3); // => OK
}).catch(x => {
  console.log(4);
}).then(x => {
  console.log('AAA'); // => OK
  console.log(AAA); // => 报错
}).catch().then(null, x => {
  console.log(5); // => OK
});

promise的应用:解决Ajax的串行、并行

< !--解决AJAX回调地狱 -->
  function queryBase() {
    return new Promise(resolve => {
      $.ajax({
        url: '/baseInfo?name=zhanglu',
        success: result => {
          resolve(result);
        }
      });
    });
  }

function queryScore(scoreId) {
  return new Promise(resolve => {
    $.ajax({
      url: '/score?id=' + scoreId,
      success: result => {
        resolve(result);
      }
    });
  });
}

function queryChinese(chinese) {
  return new Promise(resolve => {
    $.ajax({
      url: '/paiming?chin=' + chinese,
      success: result => {
        resolve(result);
      }
    });
  });
}

queryBase().then(baseInfo => {
  let scoreId = baseInfo.scoreId;
  // => then方法中如果返回的是一个Promise实例,则当前返回实例的成功或者失败状态,影响着下一个then中哪个方法会被触发执行;如果返回的是非Promise实例,则看当前方法执行是否报错,来决定下一个then中哪个方法执行;
  return queryScore(scoreId);
}).then(scoreInfo => {
  let chinese = scoreInfo.chinese;
  return queryChinese(chinese);
}).then(pai => {
  console.log('排名是:' + pai);
});


queryBase().then(baseInfo => queryScore(baseInfo.scoreId))
  .then(scoreInfo => queryChinese(scoreInfo.chinese))
  .then(pai => console.log('排名是:' + pai));

async function func() {
  let baseInfo = await queryBase();
  let scoreInfo = await queryScore(baseInfo.scoreId);
  let pai = await queryChinese(scoreInfo.chinese);
  //....
}
func();
< !--解决AJAX并行 -->
  function ajax1() {
    return new Promise(resolve => {
      $.ajax({
        url: '/api1',
        // 相当于 success: res => resolve(res)
        success: resolve
      });
    });
  }

function ajax2() {
  return new Promise(resolve => {
    $.ajax({
      url: '/api2',
      success: resolve
    });
  });
}

function ajax3() {
  return new Promise(resolve => {
    $.ajax({
      url: '/api3',
      success: resolve
    });
  });
}

// Promise.all([Promise1,Promise2,...]):all中存放的是多个Promise实例(每一个实例管理者一个异步操作),执行all方法返回的结果是一个新的Promise实例"proa"
// => 当所有Promise实例的状态都为fulfilled的时候(成功),让proa的状态也变为fulfilled,并且把所有Promise成功获取的结果,存储为成为一个数组(顺序和最开始编写的顺序一致)“result=[result1, result2, ...]”,让proa这个数组的value值等于这个数组
// => 都成功(proa状态是FUFILLED)才会通知then中第一个方法执行,只要有一个失败(proa状态是rejected),就会通知then中第二个方法或者catch中的方法执行
Promise.all([ajax1(), ajax3(), ajax2()]).then(results => {
  // => results: [result1, result3, result2]
});

Promise.race([ajax1(), ajax3(), ajax2()]).then(result => {
  // => 看哪一个Promise状态最先处理完(成功或者失败),以最先处理完的为主
});

报错问题

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    let ran = Math.random();
    ran < 0.5 ? reject(ran) : resolve(ran);
  }, 1000);
})

p1.then(result => console.log('成功: ' + result))
p1.catch(error => console.log('失败: ' + error))
p1.finally(() => console.log('嘻嘻嘻'))


console.log('-----------------------------')


let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    let ran = Math.random();
    ran < 0.5 ? reject(ran) : resolve(ran);
  }, 1000);
});

p2.then(result => console.log('成功: ' + result))
  .catch(error => console.log('失败: ' + error))
  .finally(() => console.log('哈哈哈'))

练习题

let promise1 = Promise.resolve()
  .then(res => console.log(1))
  .then(res => console.log(2))

let promise2 = new Promise(resolve => {
  setTimeout(() => {
    console.log(6)
    resolve()
  })
}).then(res => console.log(3))

async function main() {
  console.log(4)
  console.log(await Promise.all([promise2, promise1]))
  console.log(5)
  return { obj: 5 }
}

let promise3 = Promise.resolve()
  .then(res => console.log(8))
  .then(res => console.log(9))

console.log(typeof main())

// 4
// object
// 1
// 8
// 2
// 9
// 6
// 3
// [ undefined, undefined ]
// 5
原文地址:https://www.cnblogs.com/jianjie/p/13841108.html