阅读目录

  • Promise
  • 问题探讨
    • 定时嵌套
    • 图片加载
    • 加载文件
    • 异步请求
    • 肯德基
  • 异步状态
    • 状态说明
      • promise 没有使用 resolve 或 reject 更改状态时,状态为 pending
      • 当更改状态后
      • Promise 状态设置为 fulfilled ,然后执行 then 方法
    • 动态改变
  • then
    • 语法说明
    • 基础知识
    • 链式调用
    • 其它类型
      • 循环调用
      • promise
      • Thenables
  • catch
    • 使用建议
    • 处理机制
    • 定制错误
    • 事件处理
  • finally
  • Promise 实例操作
    • 异步请求
    • 图片加载
    • 定时器
  • 链式操作
    • 语法介绍
    • 链式加载
    • 操作元素
    • 异步请求
  • 扩展接口
    • resolve
    • reject
    • all 可以同时执行多个并行异步操
    • allSettled
    • race
  • 任务队列
    • 实现原理
    • 队列请求
    • 高可用封装
  • async/await
    • async
    • await
    • 加载进度
    • 类中使用
    • 其他声明
      • 函数声明
      • 函数表达式
      • 对象方法声明
      • 立即执行函数
      • 类方法中的使用
    • 错误处理
    • 并发执行

Promise

JavaScript 中存在很多异步操作,Promise 将异步操作队列化,按照期望的顺序执行,返回符合预期的结果。可以通过链式调用多个 Promise 达到我们的目的。

Promise 在各种开源库中已经实现,现在标准化后被浏览器默认支持。

Promise 是一个拥有 then 方法的对象或函数。

问题探讨

下面通过多个示例来感受一下不使用 promise 时,处理相应问题的不易,及生成了不便阅读的代码。

定时嵌套

下面是一个定时器执行结束后,执行另一个定时器,这种嵌套造成代码不易阅读

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>wgchen</title>
</head><style>div {width: 100px;height: 100px;background: yellowgreen;position: absolute;}
</style><body><div></div>
</body><script> function interval(callback, delay = 100) {let id = setInterval(() => callback(id), delay);}const div = document.querySelector("div");interval(timeId => {const left = parseInt(window.getComputedStyle(div).left);div.style.left = left + 10 + "px";if (left > 200) {clearInterval(timeId);interval(timeId => {const width = parseInt(window.getComputedStyle(div).width);div.style.width = width - 1 + "px";if (width <= 0) clearInterval(timeId);}, 10);}}, 100);
</script></html>

图片加载

下面是图片后设置图片边框,也需要使用回调函数处理,代码嵌套较复杂

function loadImage(file, resolve, reject) {const image = new Image();image.src = file;image.onload = () => {resolve(image);};image.onerror = () => {reject(new Error("load fail"));};document.body.appendChild(image);
}loadImage("888.png",image => {image.style.border = "solid 5px red";},error => {console.log(error);}
);

加载文件

下面是异步加载外部JS文件,需要使用回调函数执行,并设置的错误处理的回调函数

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>wgchen</title><link rel="shortcut icon" href="#"/>
</head>
<body></body>
<script> function load(file, resolve, reject) {const script = document.createElement("script");script.src = file;script.onload = resolve;script.onerror = reject;document.body.appendChild(script);
}load("hd.js",script => {console.log(`${script.path[0].src} 加载成功`);hd();},error => {console.log(`${error.srcElement.src} 加载失败`);}
);</script>
</html>

hd.js

function hd() {console.log("hd function run");
}

如果要加载多个脚本时需要嵌套使用,下面 wgchen.js 依赖 hd.js,需要先加载 hd.js 后加载wgchen.js

不断的回调函数操作将产生回调地狱,使代码很难维护

wgchen.js

function wgchen() {console.log("wgchen function run");hd();
}

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>wgchen</title><link rel="shortcut icon" href="#"/>
</head>
<body></body><script> function load(file, resolve, reject) {const script = document.createElement("script");script.src = file;script.onload = resolve;script.onerror = reject;document.body.appendChild(script);
}load("hd.js",script => {load("wgchen.js",script => {wgchen();},error => {console.log(`${error.srcElement.src} 加载失败`);});},error => {console.log(`${error.srcElement.src} 加载失败`);}
);</script>
</html>

异步请求

使用传统的异步请求也会产生回调嵌套的问题,下在是获取wgchen的成绩,需要经过以下两步

1、根据用户名取得 wgchen 的编号
2、根据编号获取成绩

启动PHP服务

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>wgchen</title><link rel="shortcut icon" href="#"/>
</head>
<body></body><script> function ajax(url, resolve, reject) {let xhr = new XMLHttpRequest();xhr.open("GET", url);xhr.send();xhr.onload = function() {if (this.status == 200) {resolve(JSON.parse(this.response));} else {reject(this);}};
}ajax("http://tt.cc/testData/user.php?name=wgchen", user => {// console.log(user.id);ajax(`http://tt.cc/testData/wgchen.php?id=${user["id"]}`,response => {console.log(response);});
});</script>
</html>

user.php

<?php$user = ['name'=>$_GET['name'],'file'=>'user.php','id'=>18
];echo json_encode($user);

wgchen.php

<?php$user = ['name'=>'wgchen','file'=>'wgchen.php','id'=>$_GET['id']
];echo json_encode($user);

肯德基

下面是模拟肯德基吃饭的事情,使用 promise 操作异步的方式每个阶段会很清楚

let kfc = new Promise((resolve, reject) => {console.log("肯德基厨房开始做饭");resolve("我是肯德基,你的餐已经做好了");
});
let dad = kfc.then(msg => {console.log(`收到肯德基消息: ${msg}`);return {then(resolve) {setTimeout(() => {resolve("孩子,我吃了两秒了,不辣,你可以吃了");}, 2000);}};
});
let son = dad.then(msg => {return new Promise((resolve, reject) => {console.log(`收到爸爸消息: ${msg}`);setTimeout(() => {resolve("妈妈,我和wgchen爸爸吃完饭了");}, 2000);});
});
let ma = son.then(msg => {console.log(`收到孩子消息: ${msg},事情结束`);
});

而使用以往的回调方式,就会让人苦不堪言

function notice(msg, then) {then(msg);
}
function meal() {notice("肯德基厨房开始做饭", msg => {console.log(msg);notice("我是肯德基,你的餐已经做好", msg => {console.log(`收到肯德基消息: ${msg}`);setTimeout(() => {notice("孩子,我吃了两秒了,不辣,你可以吃了", msg => {console.log(`收到爸爸消息: ${msg}`);setTimeout(() => {notice("妈妈,我和wgchen吃完饭了", msg => {console.log(`收到孩子消息: ${msg},事情结束`);});}, 2000);});}, 2000);});});
}
meal();

异步状态

Promise 可以理解为承诺,就像我们去KFC点餐服务员给我们领取餐票,这就是承诺。如果餐做好了叫我们这就是成功,如果没有办法给我们做出食物这就是拒绝。

  • 一个 promise 必须有一个 then 方法用于处理状态改变

状态说明

Promise 包含 pending、fulfilled、rejected 三种状态

  • pending 指初始等待状态,初始化 promise 时的状态
  • resolve 指已经解决,将 promise 状态设置为 fulfilled
  • reject 指拒绝处理,将 promise 状态设置为 rejected
  • promise 是生产者,通过 resolve 与 reject 函数告之结果
  • promise 非常适合需要一定执行时间的异步任务
  • 状态一旦改变将不可更改

promise 是队列状态,就像体育中的接力赛,或多米诺骨牌游戏,状态一直向后传递,当然其中的任何一个promise也可以改变状态。

promise 没有使用 resolve 或 reject 更改状态时,状态为 pending

console.log(new Promise((resolve, reject) => {})
);

当更改状态后

console.log(new Promise((resolve, reject) => {resolve("fulfilled");})
);console.log(new Promise((resolve, reject) => {reject("rejected");})
);


promise 创建时即立即执行即同步任务,then 会放在异步微任务中执行,需要等同步任务执行后才执行。

let promise = new Promise((resolve, reject) => {resolve("fulfilled");console.log("wgchen");
});promise.then(msg => {console.log(msg);
});console.log("wgchen.blog.csdn.net");

  • promise 的 then、catch、finally的方法都是异步任务
  • 程序需要将主任务执行完成才会执行异步队列任务

const promise = new Promise(resolve => resolve("success"));promise.then(alert);
alert("wgchen.blog.csdn.net");promise.then(() => {alert("wgchen");
});

Promise 状态设置为 fulfilled ,然后执行 then 方法

下例在三秒后将 Promise 状态设置为 fulfilled ,然后执行 then 方法

new Promise((resolve, reject) => {setTimeout(() => {resolve("fulfilled");}, 3000);
}).then(msg => {console.log(msg);},error => {console.log(error);}
);

状态被改变后就不能再修改了,下面先通过 resolve 改变为成功状态,表示 promise 状态已经完成,就不能使用 reject 更改状态了

new Promise((resolve, reject) => {resolve("操作成功");reject(new Error("请求失败"));
}).then(msg => {console.log(msg);},error => {console.log(error);}
);

动态改变

下例中 p2 返回了 p1 所以此时 p2 的状态已经无意义了,后面的 then 是对 p1 状态的处理。

const p1 = new Promise((resolve, reject) => {// resolve("fulfilled");reject("rejected");
});const p2 = new Promise(resolve => {resolve(p1);
}).then(value => {console.log(value);},reason => {console.log(reason);}
);
// rejected

如果 resolve 参数是一个 promise ,将会改变 promise 状态。

下例中 p1 的状态将被改变为 p2 的状态

const p1 = new Promise((resolve, reject) => {resolve(//p2new Promise((s, e) => {s("成功");}));
}).then(msg => {console.log(msg);
});
// 成功

当promise做为参数传递时,需要等待promise执行完才可以继承,下面的p2需要等待p1执行完成。

  • 因为 p2 的 resolve 返回了 p1 的 promise,所以此时 p2 的 then 方法已经是 p1 的了
  • 正因为以上原因 then 的第一个函数输出了 p1 的 resolve 的参数

const p1 = new Promise((resolve, reject) => {setTimeout(() => {resolve("操作成功");}, 2000);
});const p2 = new Promise((resolve, reject) => {resolve(p1);
}).then(msg => {console.log(msg);},error => {console.log(error);}
);

then

一个promise 需要提供一个then方法访问promise 结果,then 用于定义当 promise 状态发生改变时的处理,即promise处理异步操作,then 用于结果。

promise 就像 kfc 中的厨房,then 就是我们用户,如果餐做好了即 fulfilled ,做不了拒绝即rejected 状态。那么 then 就要对不同状态处理。

  • then 方法必须返回 promise,用户返回或系统自动返回
  • 第一个函数在 resolved 状态时执行,即执行 resolve 时执行 then 第一个函数处理成功状态
  • 第二个函数在 rejected 状态时执行,即执行 reject 时执行第二个函数处理失败状态,该函数是可选的
  • 两个函数都接收 promise 传出的值做为参数
  • 也可以使用 catch 来处理失败的状态
  • 如果 then 返回 promise ,下一个 then 会在当前 promise 状态改变后执行

语法说明

then 的语法如下,onFulfilled 函数处理 fulfilled 状态, onRejected 函数处理 rejected 状态

  • onFulfilled 或 onRejected 不是函数将被忽略
  • 两个函数只会被调用一次
  • onFulfilled 在 promise 执行成功时调用
  • onRejected 在 promise 执行拒绝时调用
promise.then(onFulfilled, onRejected)

基础知识

then 会在 promise 执行完成后执行,then 第一个函数在 resolve 成功状态执行

const promise = new Promise((resolve, reject) => {resolve("success");
}).then(value => {console.log(`解决:${value}`);},reason => {console.log(`拒绝:${reason}`);}
);
// 解决:success

then 中第二个参数在失败状态执行

const promise = new Promise((resolve, reject) => {reject("is error");
});promise.then(msg => {console.log(`成功:${msg}`);},error => {console.log(`失败:${error}`);}
);
// 失败:is error

如果只关心成功则不需要传递 then 的第二个参数

const promise = new Promise((resolve, reject) => {resolve("success");
});promise.then(msg => {console.log(`成功:${msg}`);
});
// 成功:success

如果只关心失败时状态,then 的第一个参数传递 null

const promise = new Promise((resolve, reject) => {reject("is error");
});
promise.then(null, error => {console.log(`失败:${error}`);
});
// 失败:is error

promise 传向 then 的传递值,如果then没有可处理函数,会一直向后传递

let p1 = new Promise((resolve, reject) => {reject("rejected");
})
.then()
.then(null,f => console.log(f)
);
// rejected

如果 onFulfilled 不是函数且 promise 执行成功, p2 执行成功并返回相同值

let promise = new Promise((resolve, reject) => {resolve("resolve");
});let p2 = promise.then();p2.then().then(resolve => {console.log(resolve);
});
// resolve

如果 onRejected 不是函数且 promise 拒绝执行,p2 拒绝执行并返回相同值

let promise = new Promise((resolve, reject) => {reject("reject");
});let p2 = promise.then(() => {});p2.then(null, null).then(null, reject => {console.log(reject);
});
// reject

链式调用

每次的 then 都是一个全新的 promise,默认 then 返回的 promise 状态是 fulfilled

let promise = new Promise((resolve, reject) => {resolve("fulfilled");
}).then(resolve => {console.log(resolve);
})
.then(resolve => {console.log(resolve);
});// fulfilled
// undefined

每次的 then 都是一个全新的 promise,不要认为上一个 promise状态会影响以后then返回的状态

let p1 = new Promise(resolve => {resolve();
});let p2 = p1.then(() => {console.log("wgchen");
});p2.then(() => {console.log("wgchen.blog.csdn.net");
});
console.log(p1); // Promise {<resolved>}
console.log(p2); // Promise {<pending>}// 再试试把上面两行放在 setTimeout里
setTimeout(() => {console.log(p1); // Promise {<resolved>}console.log(p2); // Promise {<resolved>}
});

then 是对上个promise 的rejected 的处理,每个 then 会是一个新的promise,默认传递 fulfilled状态

new Promise((resolve, reject) => {reject();
})
.then(resolve => console.log("fulfilled"),reject => console.log("rejected")
)
.then(resolve => console.log("fulfilled"),reject => console.log("rejected")
)
.then(resolve => console.log("fulfilled"),reject => console.log("rejected")
);# 执行结果如下rejectedfulfilledfulfilled

如果内部返回 promise 时将使用该 promise

let p1 = new Promise(resolve => {resolve();
});let p2 = p1.then(() => {return new Promise(r => {r("willem.com");});
});p2.then(v => {console.log(v); //willem.com
});

如果 then 返回 promise 时,后面的 then 就是对返回的 promise 的处理,需要等待该 promise 变更状态后执行。

let promise = new Promise(resolve => resolve());let p1 = promise.then(() => {return new Promise(resolve => {setTimeout(() => {console.log(`p1`);resolve();}, 2000);});
}).then(() => {return new Promise((a, b) => {console.log(`p2`);});
});

如果 then 返回 promise 时,返回的 promise 后面的 then 就是处理这个 promise 的

如果不 return 情况就不是这样了,即外层的 then 的 promise 和内部的 promise 是独立的两个promise

new Promise((resolve, reject) => {resolve();
})
.then(v => {return new Promise((resolve, reject) => {resolve("第二个promise");}).then(value => {console.log(value);return value;});
})
.then(value => {console.log(value);
});

这是对上面代码的优化,把内部的 then 提取出来

new Promise((resolve, reject) => {resolve();
})
.then(v => {return new Promise((resolve, reject) => {resolve("第二个promise");});
})
.then(value => {console.log(value);return value;
})
.then(value => {console.log(value);
});

其它类型

Promise 解决过程是一个抽象的操作,其需输入一个 promise 和一个值,我们表示为 [[Resolve]](promise, x),如果 x 有 then 方法且看上去像一个 Promise ,解决程序即尝试使 promise 接受 x 的状态;否则其用 x 的值来执行 promise 。

循环调用

如果 then 返回与 promise 相同将禁止执行

let promise = new Promise(resolve => {resolve();
});let p2 = promise.then(() => {return p2;
});
// TypeError: Chaining cycle detected for promise

promise

如果返加值是 promise 对象,则需要更新状态后,才可以继承执行后面的 promise

new Promise((resolve, reject) => {resolve(new Promise((resolve, reject) => {setTimeout(() => {resolve("解决状态");}, 2000);}));
})
.then(v => {console.log(`fulfilled: ${v}`);return new Promise((resolve, reject) => {setTimeout(() => {reject("失败状态");}, 2000);});},v => {console.log(`rejected: ${v}`);}
)
.catch(error => console.log(`rejected: ${error}`));

Thenables

包含 then 方法的对象就是一个 promise ,系统将传递 resolvePromise 与 rejectPromise 做为函数参数

下例中使用 resolve 或在then 方法中返回了具有 then方法的对象

  • 该对象即为 promise 要先执行,并在方法内部更改状态
  • 如果不更改状态,后面的 then promise 都为等待状态
new Promise((resolve, reject) => {resolve({then(resolve, reject) {resolve("解决状态");}});
})
.then(v => {console.log(`fulfilled: ${v}`);return {then(resolve, reject) {setTimeout(() => {reject("失败状态");}, 2000);}};
})
.then(null, error => {console.log(`rejected: ${error}`);
});

包含 then 方法的对象可以当作 promise 来使用

function ajax(url, resolve, reject) {let xhr = new XMLHttpRequest();xhr.open("GET", url);xhr.send();xhr.onload = function() {if (this.status == 200) {resolve(JSON.parse(this.response));} else {reject(this);}};
}new Promise((resolve, reject) => {ajax("http://tt.cc/testData/user.php?name=wgchen", user => {resolve(user);});
})
.then(user => {return new User(user.id);
})
.then(lessons => {console.log(lessons);
});class User {constructor(id) {this.id = id;}then(resolve, reject) {ajax("http://tt.cc/testData/user.php?name=wgchen", user => {resolve(user);});}
}

当然也可以是类

new Promise((resolve, reject) => {resolve(class {static then(resolve, reject) {setTimeout(() => {resolve("解决状态");}, 2000);}});
}).then(v => {console.log(`fulfilled: ${v}`);},v => {console.log(`rejected: ${v}`);}
);// fulfilled: 解决状态

如果对象中的 then 不是函数,则将对象做为值传递

new Promise((resolve, reject) => {resolve();
})
.then(() => {return {then: "wgchen"};
})
.then(v => {console.log(v); //{then: "wgchen"}
});

catch

下面使用未定义的变量同样会触发失败状态

let promise = new Promise((resolve, reject) => {hd;
}).then(value => console.log(value),reason => console.log(reason)
);


如果 onFulfilled 或 onRejected 抛出异常,则 p2 拒绝执行并返回拒因

let promise = new Promise((resolve, reject) => {throw new Error("fail");
});let p2 = promise.then();p2.then().then(null, resolve => {console.log(resolve + ",wgchen"); // Error: fail,wgchen
});

catch 用于失败状态的处理函数,等同于 then(null,reject){}

  • 建议使用 catch 处理错误
  • 将 catch 放在最后面用于统一处理前面发生的错误
const promise = new Promise((resolve, reject) => {reject(new Error("Notice: Promise Exception"));
}).catch(msg => {console.error(msg);
});


catch 可以捕获之前所有 promise 的错误,所以建议将 catch 放在最后。下例中 catch 也可以捕获到了第一个 then 返回 的 promise 的错误。

new Promise((resolve, reject) => {resolve();
})
.then(() => {return new Promise((resolve, reject) => {reject(".then ");});
})
.then(() => {})
.catch(msg => {console.log(msg);//.then
});

错误是冒泡操作的,下面没有任何一个 then 定义第二个函数,将一直冒泡到 catch 处理错误

new Promise((resolve, reject) => {reject(new Error("请求失败"));
})
.then(msg => {})
.then(msg => {})
.catch(error => {console.log(error);
});


catch 也可以捕获对 then 抛出的错误处理

new Promise((resolve, reject) => {resolve();
})
.then(msg => {throw new Error("这是then 抛出的错误");
})
.catch(() => {console.log("33"); // 33
});

catch 也可以捕获其他错误,下面在 then 中使用了未定义的变量,将会把错误抛出到 catch

new Promise((resolve, reject) => {resolve("success");
})
.then(msg => {console.log(a);
})
.catch(reason => {console.log(reason);
});

使用建议

建议将错误要交给 catch 处理而不是在 then 中完成,不建议使用下面的方式管理错误

new Promise((resolve, reject) => {reject(new Error("请求失败"));
}).then(msg => {console.log(msg);},error => {console.log(error);}
);

处理机制

在 promise 中抛出的错误也会被 catch 捕获

const promise = new Promise((resolve, reject) => {throw new Error("fail");
}).catch(msg => {console.log(msg.toString()+"wgchen");// Error: failwgchen
});

可以将上面的理解为如下代码,可以理解为内部自动执行 try...catch

const promise = new Promise((resolve, reject) => {try {throw new Error("fail");} catch (error) {reject(error);}
}).catch(msg => {console.log(msg.toString()); // Error: fail
});

但像下面的在异步中 throw 将不会触发 catch,而使用系统错误处理

const promise = new Promise((resolve, reject) => {setTimeout(() => {throw new Error("fail");}, 2000);
}).catch(msg => {console.log(msg + "wgchen");
});


下面在 then 方法中使用了没有定义的 hd 函数,也会抛除到 catch 执行,可以理解为内部自动执行 try...catch

const promise = new Promise((resolve, reject) => {resolve();
})
.then(() => {hd();
})
.catch(msg => {console.log(msg.toString());
});


在 catch 中发生的错误也会抛给最近的错误处理

const promise = new Promise((resolve, reject) => {reject();
})
.catch(msg => {hd();
})
.then(null, error => {console.log(error);
});

定制错误

可以根据不同的错误类型进行定制操作,下面将参数错误与404错误分别进行了处理

class ParamError extends Error {constructor(msg) {super(msg);this.name = "ParamError";}
}class HttpError extends Error {constructor(msg) {super(msg);this.name = "HttpError";}
}function ajax(url) {return new Promise((resolve, reject) => {if (!/^http/.test(url)) {throw new ParamError("请求地址格式错误");}let xhr = new XMLHttpRequest();xhr.open("GET", url);xhr.send();xhr.onload = function() {if (this.status == 200) {resolve(JSON.parse(this.response));} else if (this.status == 404) {// throw new HttpError("用户不存在");reject(new HttpError("用户不存在"));} else {reject("加载失败");}};xhr.onerror = function() {reject(this);};});
}ajax(`http://tt.cc/testData/user.php?name=wgchen`)
.then(value => {console.log(value);
})
.catch(error => {if (error instanceof ParamError) {console.log(error.message);}if (error instanceof HttpError) {alert(error.message);}console.log(error);
});

事件处理

unhandledrejection 事件用于捕获到未处理的Promise错误,下面的 then 产生了错误,但没有catch 处理,这时就会触发事件。该事件有可能在以后被废除,处理方式是对没有处理的错误直接终止。

window.addEventListener("unhandledrejection", function(event) {console.log(event.promise); // 产生错误的promise对象console.log(event.reason); // Promise的reason
});new Promise((resolve, reject) => {resolve("success");
}).then(msg => {throw new Error("fail");
});

finally

无论状态是resolve 或 reject 都会执行此动作,finally 与状态无关。

const promise = new Promise((resolve, reject) => {reject("hdcms");
})
.then(msg => {console.log("resolve");
})
.catch(msg => {console.log("reject");
})
.finally(() => {console.log("resolve/reject状态都会执行");
});


下面使用 finally 处理加载状态,当请求完成时移除加载图标。请在后台php文件中添加 sleep(2); 设置延迟响应

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>wgchen</title><link rel="shortcut icon" href="#"/>
</head>
<body><style>div {width: 100px;height: 100px;background: red;color: white;display: none;}</style><div>loading...</div>
</body>
<script>
function ajax(url) {return new Promise((resolve, reject) => {document.querySelector("div").style.display = "block";let xhr = new XMLHttpRequest();xhr.open("GET", url);xhr.send();xhr.onload = function() {if (this.status == 200) {resolve(JSON.parse(this.response));} else {reject(this);}};});
}ajax("http://tt.cc/testData/user.php?name=wgchen").then(user => {console.log(user);}).catch(error => {console.log(error);}).finally(() => {document.querySelector("div").style.display = "none";})
</script>
</html>

Promise 实例操作

异步请求

下面是将 ajax 修改为 promise 后,代码结构清晰了很多

function ajax(url) {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest();xhr.open("GET", url);xhr.send();xhr.onload = function() {if (this.status == 200) {resolve(JSON.parse(this.response));} else {reject(this);}};});
}ajax("http://tt.cc/testData/user.php?name=wgchen")
.then(user =>ajax(`http://tt.cc/testData/wgchen.php?id=${user["id"]}`))
.then(lesson => {console.log(lesson);
});


图片加载

下面是异步加载图片示例

function loadImage(file) {return new Promise((resolve, reject) => {const image = new Image();image.src = file;image.onload = () => {resolve(image);};image.onerror = reject;document.body.appendChild(image);});
}loadImage("888.png").then(image => {image.style.border = "solid 20px black";console.log("宽度:" + window.getComputedStyle(image).width);
});

定时器

下面是封装的 timeout 函数,使用定时器操作更加方便

function timeout(times) {return new Promise(resolve => {setTimeout(resolve, times);});
}timeout(3000)
.then(() => {console.log("3秒后执行");return timeout(1000);
})
.then(() => {console.log("执行上一步的promise后1秒执行");
});

封闭 setInterval 定时器并实现动画效果

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>wgchen</title><link rel="shortcut icon" href="#"/>
</head>
<body><style>div {width: 100px;height: 100px;background: yellowgreen;position: absolute;}</style><div></div>
</body>
<script>function interval(delay = 1000, callback) {return new Promise(resolve => {let id = setInterval(() => {callback(id, resolve);}, delay);});}interval(100, (id, resolve) => {const div = document.querySelector("div");let left = parseInt(window.getComputedStyle(div).left);div.style.left = left + 10 + "px";if (left >= 200) {clearInterval(id);resolve(div);}}).then(div => {interval(50, (id, resolve) => {let width = parseInt(window.getComputedStyle(div).width);div.style.width = width - 10 + "px";if (width <= 20) {clearInterval(id);}});});
</script>
</html>

链式操作

  • 第个 then 都是一个promise
  • 如果 then 返回 promse,只当 promise 结束后,才会继承执行下一个 then

语法介绍

下面是对同一个 promise 的多个 then ,每个then 都得到了同一个promise 结果,这不是链式操作,实际使用意义不大。

const promise = new Promise((resolve, reject) => {resolve("wgchen");
});promise.then(hd => {hd += "-hdcms";console.log(hd); //wgchen-hdcms
});
promise.then(hd => {hd += "-blog";console.log(hd); //wgchen-blog
});

第一个 then 也是一个promise,当没接受到结果是状态为 pending

const promise = new Promise((resolve, reject) => {resolve("wgchen");
});console.log(promise.then(hd => {hd += "-hdcms";console.log(hd);})
);

promise 中的 then 方法可以链接执行,then 方法的返回值会传递到下一个then 方法。

  • then 会返回一个promise ,所以如果有多个then 时会连续执行
  • then 返回的值会做为当前promise 的结果

下面是链式操作的 then,即始没有 return 也是会执行,因为每个then 会返回promise

new Promise((resolve, reject) => {resolve("wgchen");
})
.then(hd => {hd += "-hdcms";console.log(hd); //wgchen-hdcmsreturn hd;
})
.then(hd => {hd += "-blog";console.log(hd); //wgchen-hdcms-blog
});

then 方法可以返回一个promise 对象,等promise 执行结束后,才会继承执行后面的 then。后面的then 方法就是对新返回的promise 状态的处理

new Promise((resolve, reject) => {resolve("第一个promise");
})
.then(msg => {console.log(msg);return new Promise((resolve, reject) => {setTimeout(() => {resolve("第二个promise");}, 3000);});
})
.then(msg => {console.log(msg);
});

链式加载

使用promise 链式操作重构前面章节中的文件加载,使用代码会变得更清晰

function load(file) {return new Promise((resolve, reject) => {const script = document.createElement("script");script.src = file;script.onload = () => resolve(script);script.onerror = () => reject();document.body.appendChild(script);});
}load("hd.js")
.then(() => load("wgchen.js"))
.then(() => wgchen());


hd.js
function hd() {console.log("hd function run");
}wgchen.js
function wgchen() {console.log("wgchen function run");hd();
}

操作元素

下面使用 promise 对元素事件进行处理

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>wgchen</title><link rel="shortcut icon" href="#"/>
</head>
<body><div><h2>第九章 闭包与作用域</h2><button>收藏课程</button></div>
</body><script>
new Promise(resolve => {document.querySelector("button").addEventListener("click", e => {resolve();});
})
.then(() => {return new Promise(resolve => {setTimeout(() => {console.log("执行收藏任务");resolve();}, 2000);});
})
.then(() => {return new Promise(resolve => {setTimeout(() => {console.log("更新积分");resolve();}, 2000);});
})
.then(() => {console.log("收藏成功!奖励10积分");
})
.catch(error => console.log(errro));</script>
</html>

异步请求

下面是使用链式操作获取学生成绩

function ajax(url) {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest();xhr.open("GET", url);xhr.send();xhr.onload = function() {if (this.status == 200) {resolve(JSON.parse(this.response));} else {reject(this);}};});
}ajax("http://tt.cc/testData/user.php?name=wgchen")
.then(user => {return ajax(`http://tt.cc/testData/wgchen.php?id=${user["id"]}`);
})
.then(lesson => {console.log(lesson);
});

扩展接口

resolve

使用 promise.resolve 方法可以快速的返回一个promise对象

根据值返加 promise

Promise.resolve("wgchen").then(value => {console.log(value); //wgchen
});

下面将请求结果缓存,如果再次请求时直接返回带值的 promise

为了演示使用了定时器,你也可以在后台设置延迟响应

function ajax(url) {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest();xhr.open("GET", url);xhr.send();xhr.onload = function() {if (this.status == 200) {resolve(JSON.parse(this.response));} else {reject(this);}};});
}function query(name) {const cache = query.cache || (query.cache = new Map());// console.log(cache.has(name));if (cache.has(name)) {console.log("走缓存了");return Promise.resolve(cache.get(name));}return ajax(`http://tt.cc/testData/user.php?name=${name}`).then(response => {cache.set(name, response);console.log("没走缓存");return response;});
}query("wgchen").then(response => {console.log(response);
});setTimeout(() => {query("wgchen").then(response => {console.log(response);});
}, 1000);

如果是 thenable 对象,会将对象包装成promise处理,这与其他promise处理方式一样的

const hd = {then(resolve, reject) {resolve("wgchen");}
};
Promise.resolve(hd).then(value => {console.log(value); // wgchen
});

reject

和 Promise.resolve 类似,reject 生成一个失败的promise

Promise.reject("fail").catch(error => console.log(error));
// fail

下面使用 Project.reject 设置状态

new Promise(resolve => {resolve("wgchen");
})
.then(v => {if (v != "wgchen.blog.csdn.net") return Promise.reject(new Error("fail"));
})
.catch(error => {console.log(error);
});

all 可以同时执行多个并行异步操

使用Promise.all 方法可以同时执行多个并行异步操作,比如页面加载时同进获取课程列表与推荐课程。

  • 任何一个 Promise 执行失败就会调用 catch方法
  • 适用于一次发送多个异步操作
  • 参数必须是可迭代类型,如 Array/Set
  • 成功后返回 promise 结果的有序数组

下例中当 wgchen、willem 两个 Promise 状态都为 fulfilled 时,hd状态才为fulfilled。

const wgchen = new Promise((resolve, reject) => {setTimeout(() => {resolve("第一个Promise");}, 1000);
});const willem = new Promise((resolve, reject) => {setTimeout(() => {resolve("第二个异步");}, 1000);
});const hd = Promise.all([wgchen, willem])
.then(results => {console.log(results);
})
.catch(msg => {console.log(msg);
});// (2) ['第一个Promise', '第二个异步']

根据用户名获取用户,有任何一个用户获取不到时 promise.all 状态失败,执行 catch 方法

function ajax(url) {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest();xhr.open("GET", url);xhr.send();xhr.onload = function() {if (this.status == 200) {resolve(JSON.parse(this.response));} else {reject(this);}};});
}const api = "http://tt.cc/testData";
const promises = ["wgchen", "willem"].map(name => {return ajax(`${api}/user.php?name=${name}`);
});Promise.all(promises)
.then(response => {console.log(response);
})
.catch(error => {console.log(error);
});

可以将其他非promise 数据添加到 all 中,它将被处理成 Promise.resolve

...
const promises = [ajax(`${api}/user.php?name=wgchen`),ajax(`${api}/user.php?name=willem`),{ id: 3, name: "ycc", email: "admin@ycccms.com" }
];
...

如果某一个promise没有catch 处理,将使用promise.all 的catch处理

let p1 = new Promise((resolve, reject) => {resolve("fulfilled");
});let p2 = new Promise((resolve, reject) => {reject("rejected");
});Promise.all([p1, p2]).catch(reason => {console.log(reason);
});// rejected

allSettled

allSettled 用于处理多个promise ,只关注执行完成,不关注是否全部执行成功,allSettled 状态只会是fulfilled。

下面的 p2 返回状态为 rejected ,但 promise.allSettled 不关心,它始终将状态设置为 fulfilled 。

const p1 = new Promise((resolve, reject) => {resolve("resolved");
});
const p2 = new Promise((resolve, reject) => {reject("rejected");
});Promise.allSettled([p1, p2])
.then(msg => {console.log(msg);
})


下面是获取用户信息,但不关注某个用户是否获取不成功

function ajax(url) {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest();xhr.open("GET", url);xhr.send();xhr.onload = function() {if (this.status == 200) {resolve(JSON.parse(this.response));} else {reject(this);}};});
}const api = "http://tt.cc/testData";
const promises = [ajax(`${api}/user.php?name=willem`),ajax(`${api}/user.php?name=wgchens`)
];
Promise.allSettled(promises).then(response => {console.log(response);
});

race

使用 Promise.race() 处理容错异步,和 race 单词一样哪个 Promise 快用哪个,哪个先返回用哪个。

  • 以最快返回的promise为准
  • 如果最快返加的状态为rejected 那整个promise为rejected执行cache
  • 如果参数不是promise,内部将自动转为promise

下面将第一次请求的异步时间调整为两秒,这时第二个先返回就用第二人。

const hdcms = new Promise((resolve, reject) => {setTimeout(() => {resolve("第一个Promise");}, 2000);
});const wgchen = new Promise((resolve, reject) => {setTimeout(() => {resolve("第二个异步");}, 1000);
});Promise.race([hdcms, wgchen])
.then(results => {console.log(results);
})
.catch(msg => {console.log(msg);
});


获取用户资料,如果两秒内没有结果 promise.race 状态失败,执行catch 方法

function ajax(url) {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest();xhr.open("GET", url);xhr.send();xhr.onload = function() {if (this.status == 200) {resolve(JSON.parse(this.response));} else {reject(this);}};});
}const api = "http://tt.cc/testData";
const promises = [ajax(`${api}/user.php?name=wgchen`),new Promise((a, b) =>setTimeout(() => b(new Error("request fail")), 2000))
];Promise.race(promises).then(response => {console.log(response);
})
.catch(error => {console.log(error);
});

任务队列

实现原理

如果 then 返回 promise 时,后面的 then 就是对返回的 promise 的处理

let promise = Promise.resolve();let p1 = promise.then(() => {return new Promise(resolve => {setTimeout(() => {console.log(`p1`);resolve();}, 1000);});
});p1.then(() => {return new Promise((a, b) => {setTimeout(() => {console.log(`p2`);}, 1000);});
});


下面使用 map 构建的队列,有以下几点需要说明

  • then 内部返回的 promise 更改外部的 promise 变量
  • 为了让任务继承,执行完任务需要将 promise 状态修改为 fulfilled
function queue(nums) {let promise = Promise.resolve();nums.map(n => {promise = promise.then(v => {return new Promise(resolve => {console.log(n);resolve();});});});
}queue([1, 2, 3, 4, 5]);


下面再来通过 reduce 来实现队列

function queue(nums) {return nums.reduce((promise, n) => {return promise.then(() => {return new Promise(resolve => {console.log(n);resolve();});});}, Promise.resolve());
}queue([1, 2, 3, 4, 5]);

队列请求

下面是异步加载用户并渲染到视图中的队列实例

  • 请在后台添加延迟脚本,以观察队列执行过程
  • 也可以在任何 promise 中添加定时器观察

class User {//加载用户ajax(user) {let url = `http://tt.cc/testData/user.php?name=${user}`;return new Promise(resolve => {let xhr = new XMLHttpRequest();xhr.open("GET", url);xhr.send();xhr.onload = function() {if (this.status == 200) {resolve(JSON.parse(this.response));} else {reject(this);}};});}//启动render(users) {users.reduce((promise, user) => {return promise.then(() => {return this.ajax(user);}).then(user => {return this.view(user);});}, Promise.resolve());}//宣染视图view(user) {return new Promise(resolve => {let h1 = document.createElement("h1");h1.innerHTML = user.name;document.body.appendChild(h1);resolve();});}
}new User().render(["wgchen", "博客"]);

高可用封装

上例中处理是在队列中完成,不方便业务定制,下面将Promise处理在剥离到外部

后台请求处理类 promise_ajax_get.js

export default function(url) {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest()xhr.open('GET', url)xhr.send()xhr.onload = function() {if (this.status === 200) {resolve(this.response)} else {reject(this)}}})
}

队列处理类 promise_then.js

export default function(promises) {promises.reduce((promise, next) => promise.then(next), Promise.resolve())
}

后台脚本 users.php

<?php
$users = [1 => "小明",2 => "李四",3 => "张三"
];
sleep(1);
echo $users[$_GET['id']];

使用队列

<script type="module">import queue from './promise_then.js';import axios from './promise_ajax_get.js';queue([1, 2,3].map(v => {// console.log(`http://tt.cc/testData/user.php?id=${v}`);// http://tt.cc/testData/user.php?id=1axios(`http://tt.cc/testData/users.php?id=${v}`).then(user => console.log(user))}));
</script>

async/await

使用 async/await 是 promise 的语法糖,可以让编写 promise 更清晰易懂,也是推荐编写promise 的方式。

  • async/await 本质还是promise,只是更简洁的语法糖书写
  • async/await 使用更清晰的promise来替换 promise.then/catch 的方式

async

下面在 hd 函数前加上async,函数将返回 promise,我们就可以像使用标准Promise一样使用了。

async function hd() {return "wgchen.blog.csdn.net";
}console.log(hd());hd().then(value => {console.log(value);
});


如果有多个await 需要排队执行完成,我们可以很方便的处理多个异步队列

async function hd(message) {return new Promise(resolve => {setTimeout(() => {resolve(message);}, 2000);});
}async function run() {let h1 = await hd("wgchen");console.log(h1); // wgchenlet h2 = await hd("wgchen.blog.csdn.net");console.log(h2); // wgchen.blog.csdn.net
}run();

await

使用 await 关键词后会等待 promise 完

  • await 后面一般是promise,如果不是直接返回
  • await 必须放在 async 定义的函数中使用
  • await 用于替代 then 使编码更优雅

下例会在 await 这行暂停执行,直到等待 promise 返回结果后才继执行。

async function hd() {const promise = new Promise((resolve, reject) => {setTimeout(() => {resolve("wgchen.blog.csdn.net");}, 2000);});let result = await promise;console.log(result); // wgchen.blog.csdn.net
}
hd()

一般 await 后面是外部其它的promise对象

async function hd() {return new Promise(resolve => {setTimeout(() => {resolve("fulfilled");}, 2000);});
}async function run() {let value = await hd();console.log("wgchen.blog"); // wgchen.blogconsole.log(value); // fulfilled
}run();

下面是请求后台获取用户课程成绩的示例

function ajax(url) {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest()xhr.open('GET', url)xhr.send()xhr.onload = function() {if (this.status === 200) {resolve(this.response)} else {reject(this)}}})
}async function user() {let user = await ajax(`http://tt.cc/testData/user.php?name=八戒`);let lessons = await ajax(`http://tt.cc/testData/wgchen.php?id=${user.id=18}`);console.log(lessons);
}
user();

也可以将操作放在立即执行函数中完成

(async () => {let user = await ajax(`http://tt.cc/testData/user.php?name=八戒`);let lessons = await ajax(`http://tt.cc/testData/wgchen.php?id=${user.id=18}`);console.log(lessons);
})();

下面是使用 async 设置定时器,并间隔时间来输出内容

async function sleep(ms = 2000) {return new Promise(resolve => {setTimeout(resolve, ms);});
}async function run() {for (const value of ["willem", "wgchen"]) {await sleep();console.log(value);}
}
run();

加载进度

下面是请求后台加载用户并通过进度条展示的效果

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>wgchen</title><link rel="shortcut icon" href="#"/>
</head>
<body><style>div {height: 50px;width: 0px;background: green;}</style><div id="loading"></div>
</body>
<script>function ajax(url) {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest()xhr.open('GET', url)xhr.send()xhr.onload = function() {if (this.status === 200) {resolve(this.response)} else {reject(this)}}})}async function query(name) {return ajax(`http://tt.cc/testData/user.php?name=${name}`);}(async () => {let users = ["wgchen", "willem", "李四", "王五", "赵六"];for (let i = 0; i < users.length; i++) {await query(users[i]);let progress = (i + 1) / users.length;loading.style.width = progress * 100 + "%";}})();
</script></html>

类中使用

和 promise 一样,await 也可以操作 thenables 对象

function ajax(url) {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest()xhr.open('GET', url)xhr.send()xhr.onload = function () {if (this.status === 200) {resolve(this.response)} else {reject(this)}}})
}class User {constructor(name) {this.name = name;}then(resolve, reject) {let user = ajax(`http://tt.cc/testData/user.php?name=${this.name}`);resolve(user);}
}async function get() {let user = await new User("ycc");console.log(user); // {"name":"ycc","file":"user.php","id":18}
}
get();

类方法也可以通过 async 与 await 来操作promise

function ajax(url) {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest()xhr.open('GET', url)xhr.send()xhr.onload = function () {if (this.status === 200) {resolve(this.response)} else {reject(this)}}})
}class User {constructor() {}async get(name) {let user = JSON.parse(await ajax(`http://tt.cc/testData/user.php?name=${name}`));user.url = name+".blog.csdn.net";user.name += "+=";return user;}
}new User().get("wgchen").then(resolve => {console.log(resolve);
});

其他声明

函数声明

function ajax(url) {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest()xhr.open('GET', url)xhr.send()xhr.onload = function () {if (this.status === 200) {resolve(this.response)} else {reject(this)}}})
}async function get(name) {return await ajax(`http://tt.cc/testData/user.php?name=${name}`);
}
get("willem").then(user => {console.log(user);
});

函数表达式

let get = async function(name) {return await ajax(`http://tt.cc/testData/user.php?name=${name}`);
};get("wgchen").then(user => {console.log(user);
});

对象方法声明

let hd = {async get(name) {return await ajax(`http://tt.cc/testData/user.php?name=${name}`);}
};hd.get("ycc").then(user => {console.log(user);
});

立即执行函数

(async () => {let user = JSON.parse(await ajax(`http://tt.cc/testData/user.php?name=wg`));let lessons = await ajax(`http://tt.cc/testData/wgchen.php?id=${user.id}`);console.log(lessons);
})();

类方法中的使用

class User {async get(name) {return await ajax(`http://tt.cc/testData/user.php?name=${name}`);}
}
let user = new User().get("wg").then(user => {console.log(user);
});

错误处理

async 内部发生的错误,会将必变 promise 对象为 rejected 状态,所以可以使用 catch 来处理。

async function hd() {console.log(wgchen);
}hd().catch(error => {throw new Error(error);
});


下面是异步请求数据不存在时的错误处理

async function get(name) {return await ajax(`http://tt.cc/testData/user.php?name=${name}`);
}get("wg").catch(error => {alert("用户不存在");
});


如果 promise 被拒绝将抛出异常,可以使用 try...catch 处理错误

async function get(name) {try {let user = await ajax(`http://localhost:8888/php/user.php?name=${name}`);console.log(user);} catch (error) {alert("用户不存在");}
}
get("wg");


多个 await 时当前面的出现失败,后面的将不可以执行

async function hd() {await Promise.reject("fail");await Promise.resolve("success").then(value => {console.log(value);});
}hd();


如果对前一个错误进行了处理,后面的 await 可以继续执行

async function hd() {await Promise.reject("fail").catch(e => console.log(e));await Promise.resolve("success").then(value => {console.log(value);});
}
hd();


也可以使用 try...catch 特性忽略不必要的错误

async function hd() {try {await Promise.reject("fail");} catch (error) {}await Promise.resolve("success").then(value => {console.log(value);});
}
hd();


也可以将多个 await 放在 try...catch 中统一处理错误

async function hd(name) {const host = "http://localhost:8888/php";try {const user = await ajax(`${host}/user.php?name=${name}`);const lessons = await ajax(`${host}/user.php?id=${user.id}`);console.log(lessons);} catch (error) {console.log("用户不存在");}
}
hd("wg");

并发执行

有时需要多个await 同时执行,有以下几种方法处理,下面多个await 将产生等待

async function p1() {return new Promise(resolve => {setTimeout(() => {console.log("wgchen");resolve();}, 2000);});
}
async function p2() {return new Promise(resolve => {setTimeout(() => {console.log("willem");resolve();}, 2000);});
}
async function hd() {await p1();await p2();
}
hd();

使用 Promise.all() 处理多个promise并行执行

async function hd() {await Promise.all([p1(), p2()]);
}
hd();


让 promise 先执行后再使用 await 处理结果

async function hd() {let h1 = p1();let h2 = p2();await h1;await h2;
}hd();

JS基础 Promise相关推荐

  1. (七)JS基础知识四(搞定异步)【三座大山之三,必考!!!】

    JS基础知识四(异步) 提问 单线程 应用场景 callback hell和promise 提问 同步和异步的区别是什么 手写用Promise加载一张图片 前端使用的异步的场景有哪些 单线程 JS是单 ...

  2. 区块链教程(四):搭建私链、web3.js基础

    注:本教程为技术教程,不谈论且不涉及炒作任何数字货币 区块连教程(一):前置知识-linux补充 区块链教程(二):基础概念介绍 区块链教程(三):Solidity编程基础 区块链教程(四):搭建私链 ...

  3. JS 基础知识点及常考面试题(二)

    JS 基础知识点及常考面试题(二) 涉及面试题:== 和 === 有什么区别? 对于 == 来说,如果对比双方的类型不一样的话,就会进行类型转换,这也就用到了我们上一章节讲的内容. 假如我们需要对比 ...

  4. 前端工程师面试问题归纳(一、问答类html/css/js基础)

    一.参考资源 1.前端面试题及答案整理(一) 2.2017年前端面试题整理汇总100题 3.2018最新Web前端经典面试试题及答案 4.[javascript常见面试题]常见前端面试题及答案 5.W ...

  5. cytoscape.js基础篇

    cytoscape.js基础篇 cytoscape.js 包引用 版本信息 Citation Funding 基础篇 cytoscape.js变量描述 位置 Elements JSON 节点属性说明 ...

  6. Node.js基础笔记

    Node.js基础笔记 1. NodeJS入门基础 1.1 NodeJS 介绍 1.1.1 NodeJS 是什么 1.1.2 NodeJS 的优势 1.1.3 NodeJS 的技术特性 1.1.4 N ...

  7. 3、JS基础知识-前端面试

    文章目录 常见JS特效 1.在页面的盒子中显示鼠标坐标 2.滚动条的距离 3.动画函数的封装 4.轮播图的实现 5.图片放大效果 6.点击Tab栏的切换 7.精灵图的使用 8.字体图标的使用 < ...

  8. 构建node.js基础镜像_我如何使用Node.js构建工作抓取网络应用

    构建node.js基础镜像 by Oyetoke Tobi Emmanuel 由Oyetoke Tobi Emmanuel 我如何使用Node.js构建工作抓取网络应用 (How I built a ...

  9. 构建node.js基础镜像_在Android上构建Node.js应用程序

    构建node.js基础镜像 by Aurélien Giraud 通过AurélienGiraud 在Android上构建Node.js应用程序-第1部分:Termux,Vim和Node.js (Bu ...

最新文章

  1. C 线程池(不稳定的方案)
  2. JBOSS的管理员账号和密码设定
  3. XmlValidatingReader
  4. NTU生活:NTU景点
  5. 【Pygame小游戏】首月破亿下载 一款高度融合了「超休闲游戏特性」的佳作~
  6. java定时器偶尔重复推送_在Java中创建重复的定时器提醒
  7. 剖析Hadoop和Spark的Shuffle过程差异(一)
  8. mycat连接mysql时间_Mycat连接MySQL 8时的注意事项
  9. windows环境下ELK平台搭建
  10. 交叉验证选择最佳参数_如何为您的公司选择最佳的身份验证即服务提供商
  11. 微信小程序开发学习笔记006--微信小程序组件详解02
  12. Java基础视频笔记(四):泛型
  13. DupanTools第三方不限速下载工具
  14. mp4格式的视频流传输下实现边下边播
  15. 2023最新显卡天梯图 2023显卡天梯排行榜 显卡天梯图2023年1月
  16. flash air java_Flash Air 打包安卓 ane
  17. 关于AS5 SSH支持key认证的完全解决方案
  18. CentOS 安装 kafka 扩展
  19. 计算机考试的话语,为考试加油的暖心句子 期末考试加油鼓励的话
  20. 汇通达再度冲刺港交所上市:整体毛利率低下,已有股东提前退出

热门文章

  1. 【视频教程】UG NX CAM 加工模块二次开发 视频教程
  2. 《史蒂夫.鲍尔默传》读书笔记
  3. Unity报错 cleaning the mesh failed
  4. 2018年寒假假期安排以大二下学期学习安排
  5. 一个月薪1万的程序员给我上了震撼一课
  6. c语言许愿树编程,许愿树(五)
  7. 技术沙龙|京东云区块链进校园-京东云深圳大学线下沙龙分享回顾
  8. 浅析STM32 Bootloader设计
  9. 七夕祝福网页制作_七夕宠粉高甜预警!一叶子AGATHA大玩跨界“秀恩爱”
  10. Go语言Delve调试